2011-09-19 150 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。我喜歡@康拉德的內部連接解決方​​案,最簡單也可能是性能最好的,可能我會用它,只是將它作爲另一種選擇。