SQL: перечисление многих ко многим из одного запроса - PullRequest
2 голосов
/ 17 ноября 2010

У меня есть 3 таблицы, представляющие «Пользователи», «Роли» и многие «многие ко многим» UsersInRoles »с ключами: UserId, RoleId;соответствующие столбцы: UserName, RoleName

В html-приложении администратора я хочу показать список всех пользователей и роли, в которых они находятся. Из SQL я пытаюсь создать один запрос, который будет возвращать этоИнформация.Список ролей должен быть разделен, чтобы я мог выполнять соответствующие манипуляции с презентацией (в зависимости от платформы презентации, например, заменить разделитель тегом BR).

Использование одного выбора для списка пользователей, а затем отдельного выбора для каждого пользователяполучить роли просто, но попытка создать единый выбор, который выводит нижеследующее, поставила меня в тупик.

UserId  UserName  Roles
------  --------  -----
1       user1     Admin,Guest,PowerUser
2       user2     Guest
3       user3
4       user4     PowerUser,Guest

Заранее спасибо,
--Ed

- EDIT -
теперь работает со следующим запросом (спасибо всем, особенно Raymund & его блог ):

WITH RolesList AS
(
  SELECT u.UserName,
  (
    SELECT r.RoleName + ',' AS 'data()'
    FROM Roles r
    JOIN UsersInRoles ur ON ur.RoleId = r.RoleId
    WHERE ur.UserId = u.UserId FOR XML PATH('')
  ) AS RolesCSV
  FROM Users u
) SELECT UserName, LEFT(RolesCSV, LEN(RolesCSV)-1) AS RolesCSV FROM RolesList

Ответы [ 3 ]

3 голосов
/ 17 ноября 2010

Вот ваше решение:

Преобразование / синтаксический анализ строк в столбец с разделителями в SQL

EDIT

Если вам нужна дополнительная ясность, вот ответ

WITH UserList as
(
SELECT UserID, UserName,
(SELECT 
RoleName + ',' AS 'data()'
FROM Roles
INNER JOIN 
UsersInRoles 
ON 
Roles.RoleID = UsersInRoles.RoleID
WHERE
UsersInRoles.UserID = Users.UserID FOR XML PATH('')) AS RoleCSV
FROM Users
)
SELECT UserID, UserName, LEFT(RoleCSV, LEN(RoleCSV)-1) as RoleCSV from UserList
2 голосов
/ 17 ноября 2010

SQL Server 2005 +:

SELECT u.user_id,
       u.username,
       STUFF(ISNULL(SELECT ', ' + r.role_name
                      FROM USERSINROLES uir
                  JOIN ROLES r ON r.role_id = uir.role_id
                 WHERE uir.user_id = u.user_id
              GROUP BY r.role_name
                   FOR XML PATH ('')), ''), 1, 2, '')
  FROM USERS u
0 голосов
/ 17 ноября 2010

Так как @OMG Ponies указал, что мой первоначальный «поворотный» ответ не отвечал требованиям этого вопроса, позвольте мне обратиться к нему за решением. Однако я все же хотел бы упомянуть альтернативу:

Список ролей должен быть разделен так что я могу сделать соответствующее манипулирование презентацией (в зависимости на презентационной платформе, как заменить разделитель с меткой BR).

Если вы предоставляете разделители только для того, чтобы ваш уровень представления мог (потенциально) разбить объединенные значения обратно на части, почему бы просто не вернуть их в отдельном наборе или просто вернуть результат объединения в таблицу «многие ко многим» (оставив вам кучу дубликатов), и просто обработать результаты на уровне презентации?

Два аргумента для этого:

  • Работа со строками в чем-то вроде C # обычно быстрее / менее болезненна, чем в SQL
  • Лучшее разделение задач - ваш уровень данных может просто возвращать набор ролей для каждого пользователя, не заботясь о том, как эти данные будут представлены

В вашем случае ни один из них не может быть убедительным, но я надеюсь, что я дал пищу для размышлений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...