2011-09-19 298 views
2

3个表定义如下:帮我看看这个SQL查询

用户

User_ID  INT 
First_Name VARCHAR 
Last_Name VARCHAR 
Email  VARCHAR 

角色

Role_ID  INT 
Role_Name VARCHAR 
Access_Level INT 

Roles_Users

User_ID  INT 
Role_ID  INT 

Roles_UsersUsersRoles之间的多对多链接表。我想拉回来下列信息:

First_Name, Last_Name, Email, Role_Name 

我至今是:

SELECT 
    U.First_Name, 
    U.Last_Name, 
    U.Email, 
    R.Name AS Role_Name 
FROM Users U 
INNER JOIN Roles_Users RU ON U.User_ID = RU.User_ID 
INNER JOIN Roles R ON RU.Role_ID = R.Role_ID 

最棘手的部分(至少对我来说)是,我只想拉了回来Role_Name与该特定用户的MIN(Access_Level)。所以基本上我想要拉的记录集将使每个用户只列出一次访问级别最低的角色名称。

我确定这很简单,但它现在只是将我绊倒。

由于

+0

这就是为什么我讨厌*多对多*表。 –

回答

3

你可以结合使用的CTE(公用表表达式)与ROW_NUMBER窗函数是这样的:

;WITH MinAccessData AS 
(
SELECT 
    U.First_Name, 
    U.Last_Name, 
    U.Email, 
    R.Name AS Role_Name, 
    ROW_NUMBER() OVER(PARTITION BY U.User_ID ORDER BY R.Access_Level) AS RowNum 
FROM Users U 
INNER JOIN Roles_Users RU ON U.User_ID = RU.User_ID 
INNER JOIN Roles R ON RU.Role_ID = R.Role_ID 
) 
SELECT * 
FROM MinAccessData 
WHERE RowNum = 1 

的CTE “分区” 您的数据由User_ID,例如每个用户都得到一个“分区”。在该分区内部,角色按Access_level排序,最小的是第一个 - 因此它得到RowNum = 1 - 对于每个用户。

因此,您然后从该CTE中选择所有那些条目,其中RowNum = 1 - 为每个用户提供所有具有最小Access_Level值的条目。

+1

+1我在看到你之前发过一些非常相似的东西。 –

2

替代品没有CTE(只是有你的盒子的另一种工具)

SELECT 
    U.First_Name, 
    U.Last_Name, 
    U.Email, 
    R.Name AS Role_Name 
FROM Users U 
INNER JOIN Roles_Users RU ON U.User_ID = RU.User_ID 
INNER JOIN Roles R ON RU.Role_ID = R.Role_ID 
INNER JOIN (SELECT 
       MIN(r.Access_Level) access_level, 
       ru.UserID, 
      FROM Roles r 
       INNER JOIN Roles_Users ru 
       ON r.Role_ID = ru.Role_ID 
      GROUP BY UserID 
       ) minAccess 
ON ru.UserId = minAccess.UserId 
    and ru. 
    ON r.access_level = minAccess .access_level 

您也可以使用CROSS APPLY

SELECT 
     U.First_Name, 
     U.Last_Name, 
     U.Email, 
     R.Name AS Role_Name 
    FROM Users U 
    CROSS APPLY (SELECT TOP 1 
        Role_Name 
        FROM Roles_Users RU ON U.User_ID = RU.User_ID 
         INNER JOIN Roles R ON RU.Role_ID = R.Role_ID 
        WHERE u.user_id = ru.user_id 
        ORDER BY 
         Access_Level desc 
        ) 
+1

CTE有什么问题? :-) –

+1

作品 - 但仅限于“前1名” - 尝试使用此.....做“前3名”或“前5名”这就是为什么我** ** ** CTE的! –

+0

@Aaron。没有什么,除了marc_s更快哦,它很高兴知道便携式解决方案 –

0

没有测试,但它是这样的

SELECT 
    U.First_Name, 
    U.Last_Name, 
    U.Email, 
    R.Role_Name 
FROM Users U 
JOIN Roles_Users RU ON U.User_ID = RU.User_ID 
JOIN (
     SELECT ROLE_ID, MIN(ROLE_NAME) ROLE_NAME 
     FROM ROLES 
     GROUP BY ROLE_ID 
     HAVING ACCESS_LEVEL = MIN(ACCESS_LEVEL) 
    ) R ON RU.Role_ID = R.Role_ID 
0
SELECT Users.*, Roles.* 
FROM 
    Users 
    JOIN Roles_Users ON Users.User_ID = Roles_Users.User_ID 
    JOIN Roles ON Roles.Role_ID = Roles_Users.Role_ID 
WHERE 
    Access_Level = (
     SELECT MIN(Access_Level) 
     FROM 
      Roles_Users 
      JOIN Roles ON Roles.Role_ID = Roles_Users.Role_ID 
     WHERE Users.User_ID = Roles_Users.User_ID 
    ) 

注意:这个w生病没有列出没有任何作用的用户。

1

相关子查询:

SELECT 
    U.First_Name, 
    U.Last_Name, 
    U.Email, 
(
select top 1 R.RName from Roles_Users RU ON U.User_ID = RU.User_ID 
INNER JOIN Roles R ON RU.Role_ID = R.Role_ID 
ORDER BY R.Access_Level 
) 
AS Role_Name 
FROM Users U 

在使用子查询我看来是更容易阅读和写作。在这段代码中,相关子查询将返回每行1x。我喜欢@康拉德的内部连接解决方​​案,最简单也可能是性能最好的,可能我会用它,只是将它作为另一种选择。