Помогите мне с этим SQL-запросом - PullRequest
2 голосов
/ 19 сентября 2011

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_Users - это таблица связей «многие ко многим» между Users и Roles.Я хочу получить следующую информацию:

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) для этого конкретного пользователя.Так что, в основном, в наборе записей, который я хочу получить, каждый пользователь будет указан только один раз с именем роли с самым низким уровнем доступа.

Я уверен, что это довольно просто, но сейчас я просто озадачен.

Спасибо

Ответы [ 5 ]

3 голосов
/ 19 сентября 2011

Вы можете использовать CTE (Common Table Expression) в сочетании с оконной функцией 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 голосов
/ 19 сентября 2011

Альтернативы без 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 голос
/ 19 сентября 2011

Коррелированный подзапрос:

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, самое простое и, вероятно, наиболее эффективное, и, вероятно, то, что я бы использовал, просто предоставив это в качестве другого варианта.

0 голосов
/ 19 сентября 2011
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
    )

ПРИМЕЧАНИЕ. В этом списке не отображаются пользователи без роли.

0 голосов
/ 19 сентября 2011

Не проверено, но выглядит примерно так

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...