展平/导出的平铺关系数据

时间:2009-04-14 10:33:25

标签: sql ms-access

假设我们在MS Access数据库中有两个表:

服务用户:

| ID | Name | Other details... |
| 1  | Joe  | Blah...          |
| 2  | Fred | Qwerty...        |
| 3  | Bob  | Something else...|

提供服务的团队:

| ID | TeamID | UserID |
| 1  | T1     | 1      |
| 2  | T2     | 1      |
| 3  | T2     | 2      |
| 4  | T3     | 2      |
| 5  | T3     | 3      |

我需要生成一个摘要查询,为每个用户生成一行,前几个团队(由TeamID)分配坐在不同的列中。像:

查询:

| UserID | Name | Team1 | Team2 |
| 1      | Joe  | T1    | T2    |
| 2      | Fred | T2    | T3    |
| 3      | Bob  | T3    | Null  |

我可以使用子选择查询中的max()来获取Team1列,但是我对如何实现Team2,Team3等有一个完整的心理障碍(是的,我知道如果分配了更多的团队对于不是我创建列的用户,查询将丢失该信息:这不是问题。)

编辑:为了澄清,查询中的列数将被修复(在实际查询中,将始终为7)。如果团队数量少于列数,则其他列应为Null(如示例所示)。如果团队数量多于列数,则此摘要中仅显示前7个团队。

编辑2 - 可行的解决方案......:

我试过......

SELECT UserTable.ID As UID, UserTable.Name, 
(SELECT TOP 1 TeamID FROM TeamTable WHERE UserTable.ID = TeamTable.UserID
ORDER BY TeamID) As Team1
FROM UserTable

......工作正常。不幸的是...

SELECT UserTable.ID As UID, UserTable.Name, 
(SELECT TOP 1 TeamID FROM TeamTable WHERE UserTable.ID = TeamTable.UserID
ORDER BY TeamID) As Team1,
(SELECT TOP 1 TeamID FROM TeamTable WHERE UserTable.ID = TeamTable.UserID
AND TeamID <> Team1 ORDER BY TeamID) As Team2
FROM UserTable

...为Team1抛出一个参数框。关于如何从jet查询跳过第一个/第二个/ etc ...值的想法?

4 个答案:

答案 0 :(得分:2)

交叉表查询应该适合。

查询1:称为TeamUser

SELECT Teams.UserID, ServiceUsers.SName, Teams.TeamID
FROM ServiceUsers 
INNER JOIN Teams ON ServiceUsers.ID = Teams.UserID;

交叉表

TRANSFORM First(TeamUser.TeamID) AS FirstOfTeamID
SELECT TeamUser.UserID, TeamUser.SName
FROM TeamUser
GROUP BY TeamUser.UserID, TeamUser.SName
PIVOT TeamUser.TeamID;

编辑以回应评论

应该可以组合这两个查询并使用联合查询来减少条目数。

TRANSFORM First(t.ATeamID) AS FirstOfATeamID
SELECT t.UserID, s.SName
FROM (SELECT Teams.UserID, First(Teams.ID) AS FirstOfID, 
             First(Teams.TeamID) AS ATeamID, "1st" As TeamCount
      FROM Teams
      GROUP BY Teams.UserID
      UNION 
      SELECT Teams.UserID, First(Teams.ID) AS FirstOfID, 
             First(Teams.TeamID) AS ATeamID, "2nd" As TeamCount
      FROM Teams
      WHERE ID Not In (SELECT First(Teams.ID) 
                       FROM Teams GROUP BY Teams.UserID)
      GROUP BY Teams.UserID) t
INNER JOIN ServiceUsers s ON t.UserID = s.ID
GROUP BY t.UserID, s.SName
PIVOT t.TeamCount

答案 1 :(得分:2)

首先,您需要一个查询,将1到N之间的数字分配给与用户关联的团队:

SELECT UserID, TeamID, 
       (SELECT count(*) FROM TeamTable t2 WHERE t2.TeamID <= TeamTable.TeamID and t2.UserID=TeamTable.UserID) AS position 
FROM TeamTable 
ORDER BY TeamID

让我们调用此查询TeamList。 现在,您可以在主查询中使用此查询,通过调用它7次,每次过滤不同的位置:

SELECT UserTable.ID As UID, UserTable.Name, 
   (SELECT TeamID FROM TeamList WHERE UserTable.ID = TeamList.UserID AND position=1) As Team1,
   (SELECT TeamID FROM TeamList WHERE UserTable.ID = TeamList.UserID AND position=2) As Team2,
   [...]
FROM UserTable 

您可以在一个查询中汇总所有这些内容,但定义TeamList查询并多次调用它更为实际。 另请注意,这种编号基于TeamID的顺序。您可以通过更改TeamList查询来选择其他订单,但您选择的字段必须为每个团队具有不同的唯一值(或者&lt; =比较将生成错误的数字)。

显然,有大量行表现会很糟糕,但几百行可能是可以接受的。你必须尝试。

答案 2 :(得分:0)

我不是MS-Access专家,但是有没有办法在子查询中跳过行?因此,对于Team1,您跳过0行,对于Team2,您跳过1行,等等。每次使用MAX()从子查询中的行中选择最大值。由于每次只有一行,每次都会得到另一个最大值(如果你对子查询结果进行排序)。

一般的想法是,每个子查询(对于Team1,Team2,...)返回的值较少,这样MAX()函数每次都返回另一个值。

看一下MS-Access SQL的可能性,你只能使用TOP。但是你必须知道你的子查询有多少行。我觉得这是不可能的。假设您确实知道这一点并且用户是3个团队的成员,则每个团队的子查询包括SELECT TOP(3) ...SELECT TOP(2) ...SELECT TOP(1)...

答案 3 :(得分:0)

只需将Team1作为子查询,以便在获取Team2时引用Team1之前填充它;

SELECT UserTable.ID As UID,UserTable.Name,T1.Team1,
(SELECT TOP 1 TeamID FROM TeamTable WHERE UserTable.ID = TeamTable.UserID AND TeamID <> T1.Team1 ORDER BY TeamID) As Team2 
FROM UserTable 
LEFT OUTER JOIN
    (
        SELECT UserTable.ID As UID,  
           (SELECT TOP 1 TeamID FROM TeamTable WHERE UserTable.ID = TeamTable.UserID 
        ORDER BY TeamID) As Team1, 
        FROM UserTable 
    )T1 ON UserTable.ID=T1.UID

这也适用于CTE:

;With CTE_Team1 As (
        SELECT UserTable.ID As UID,  
           (SELECT TOP 1 TeamID FROM TeamTable WHERE UserTable.ID = TeamTable.UserID ORDER BY TeamID) As Team1, 
        FROM UserTable )
SELECT UserTable.ID As UID,UserTable.Name,T1.Team1,
   (SELECT TOP 1 TeamID FROM TeamTable WHERE UserTable.ID = TeamTable.UserID AND TeamID <> T1.Team1 ORDER BY TeamID) As Team2 
FROM UserTable 
LEFT OUTER JOIN
CTE_Team1 T1 ON UserTable.ID=T1.UID