【问题标题】:Help me with this SQL Query帮我处理这个 SQL 查询
【发布时间】:2011-09-19 16:36:59
【问题描述】:

3个表定义如下:

用户

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_NameMIN(Access_Level)。因此,基本上我想要提取的记录集将每个用户仅列出一次,其访问级别最低的角色名称。

我确信这很简单,但现在只是难倒我。

谢谢

【问题讨论】:

  • 这就是我讨厌多对多表的原因。

标签: sql sql-server-2008


【解决方案1】:

您可以将 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 值的所有条目。

【讨论】:

    【解决方案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
    

    您也可以使用交叉应用

     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” - 尝试使用此“前 3”或“前 5”......这就是我 喜欢 CTE 的原因!
    • @Aaron。除了 marc_s 更快哦,很高兴知道便携式解决方案
    • 好的,我没有查看编辑历史记录,您的开场评论表明避免 CTE 听起来很可取,仅此而已。 :-)
    • @Marc_s CROSS APPLY 可以在没有 CTE 的情况下完成 TOP N。并不是说我反对 CTE。 (事实上​​,我目前正在编写一个大约有 5 个的 Oracle 查询)
    【解决方案3】:

    相关子查询:

    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
    

    在我看来,使用子查询更容易读写。在此代码中,相关子查询将每返回行执行 1 次。我喜欢@Conrad 的内部连接解决方​​案,它最简单,可能也是性能最高的,可能是我会使用的,只是将其作为另一种选择。

    【讨论】:

      【解决方案4】:

      未测试,但它是这样的

      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
      

      【讨论】:

        【解决方案5】:
        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
            )
        

        注意:这不会列出没有任何角色的用户。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-01-06
          • 1970-01-01
          • 1970-01-01
          • 2021-09-12
          • 1970-01-01
          • 2015-03-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多