Справка по SQL-запросу с таблицей мостов - PullRequest
6 голосов
/ 18 ноября 2008

Я работаю с существующей базой данных и пытаюсь написать SQL-запрос, чтобы получить всю информацию об учетной записи, включая уровни разрешений. Это для аудита безопасности. Мы хотим выложить всю эту информацию понятным способом, чтобы ее было легко сравнивать. Моя проблема в том, что для разрешений есть таблица мостов / ссылок, поэтому для каждого пользователя существует несколько записей. Я хочу получить обратно результаты со всеми разрешениями для одного пользователя в одной строке. Вот пример:

Table_User:
UserId   UserName
1        John
2        Joe
3        James

Table_UserPermissions:
UserId   PermissionId   Rights
1        10             1
1        11             2
1        12             3
2        11             2
2        12             3
3        10             2

PermissionID ссылается на таблицу с названием разрешения и тем, что оно делает. Право это как 1 = вид, 2 = изменить и т. Д.

Что я получаю от базового запроса для пользователя 1:

UserId UserName PermissionId Rights
1      John     10           1
1      John     11           2
1      John     12           3

Что бы я хотел примерно так:

UserId UserName Permission1 Rights1 Permission2 Right2 Permission3 Right3
1      John     10          1       11          2      12          3

В идеале я хотел бы, чтобы это было для всех пользователей. Самая близкая вещь, которую я нашел, - это функция Pivot в SQL Server 2005. http://geekswithblogs.net/lorint/archive/2006/08/04/87166.aspx Проблема в том, что я могу сказать, что мне нужно назвать каждый столбец для каждого пользователя, и я не уверен, как получить уровень прав. С реальными данными у меня около 130 пользователей и 40 разных разрешений.

Есть ли другой способ с помощью только sql, что я могу сделать это?

Ответы [ 8 ]

3 голосов
/ 18 ноября 2008

Вы можете сделать что-то вроде этого:

select userid, username
,      max(case when permissionid=10 then rights end) as permission10_rights
,      max(case when permissionid=11 then rights end) as permission11_rights
,      max(case when permissionid=12 then rights end) as permission12_rights
from   userpermissions
group by userid, username;

Вы должны явно добавить аналогичный столбец max (...) для каждого ID разрешения.

1 голос
/ 19 ноября 2008

Если вы используете MySQL, я бы предложил использовать group_concat () , как показано ниже.

select UserId, UserName, 
       group_concat(PermissionId) as PermIdList,
       group_concat(Rights SEPARATOR ',') as RightsList
from Table_user join Table_UserPermissions on 
     Table_User.UserId = Table_UserPermissions.UserId=
GROUP BY Table_User.UserId

Это вернет

UserId UserName PermIdList  RightsList
1      John     10,11,12    1,2,3

Быстрый поиск в Google по запросу "mssql group_concat" выявил несколько различных хранимых процедур ( I ), ( II ) для MSSQL, которые могут достичь того же поведения.

1 голос
/ 18 ноября 2008

Краткий ответ:

номер

Вы не можете динамически добавлять столбцы в ваш запрос.

Помните, что SQL - это язык на основе множеств. Вы запрашиваете наборы и объединяете наборы.

То, что вы выкапываете, является рекурсивным списком и требует, чтобы список был соединен по горизонтали, а не по вертикали.

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

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

Что-то вроде:

SELECT Table_User.userID, userName, permissionid, rights
FROM Table_User
        LEFT JOIN Table_UserPermissions ON Table_User.userID =Table_UserPermissions.userID
ORDER BY userName

А затем отобразить все разрешения для каждого пользователя, используя что-то вроде (Python):

userID = recordset[0][0]
userName = recordset[0][1]
for row in recordset:
   if userID != row[0]:
       printUserPermissions(username, user_permissions)
       user_permissions = []
       username = row[1]
       userID = row[0]

    user_permissions.append((row[2], row[3]))

printUserPermissions(username, user_permissions)
0 голосов
/ 21 декабря 2012

Для этого типа преобразования данных вам необходимо выполнить как UNPIVOT, так и затем PIVOT данных. Если вам известны значения, которые вы хотите преобразовать, то вы можете жестко закодировать запрос с помощью статического центра, в противном случае вы можете использовать динамический sql.

Создание таблиц:

CREATE TABLE Table_User
    ([UserId] int, [UserName] varchar(5))
;

INSERT INTO Table_User
    ([UserId], [UserName])
VALUES
    (1, 'John'),
    (2, 'Joe'),
    (3, 'James')
;

CREATE TABLE Table_UserPermissions
    ([UserId] int, [PermissionId] int, [Rights] int)
;

INSERT INTO Table_UserPermissions
    ([UserId], [PermissionId], [Rights])
VALUES
    (1, 10, 1),
    (1, 11, 2),
    (1, 12, 3),
    (2, 11, 2),
    (2, 12, 3),
    (3, 10, 2)
;

Статический PIVOT:

select *
from
(
  select userid,
    username,
    value,
    col + '_'+ cast(rn as varchar(10)) col
  from
  (
    select u.userid,
      u.username,
      p.permissionid,
      p.rights,
      row_number() over(partition by u.userid 
                        order by p.permissionid, p.rights) rn
    from table_user u
    left join Table_UserPermissions p
      on u.userid = p.userid
  ) src
  unpivot
  (
    value
    for col in (permissionid, rights)
  ) unpiv
) src
pivot
(
  max(value)
  for col in (permissionid_1, rights_1, 
              permissionid_2, rights_2, 
              permissionid_3, rights_3)
) piv
order by userid

См. SQL Fiddle с демоверсией

Динамический PIVOT:

Если у вас есть неизвестное число permissionid s и rights, то вы можете использовать динамический sql:

DECLARE 
    @query  AS NVARCHAR(MAX),
    @colsPivot as  NVARCHAR(MAX)

select @colsPivot = STUFF((SELECT  ',' 
                      + quotename(c.name +'_'+ cast(t.rn as varchar(10)))
                    from
                    (
                      select row_number() over(partition by u.userid 
                                order by p.permissionid, p.rights) rn
                      from table_user u
                      left join Table_UserPermissions p
                        on u.userid = p.userid
                    ) t
                    cross apply sys.columns as C
                   where C.object_id = object_id('Table_UserPermissions') and
                         C.name not in ('UserId')
                   group by c.name, t.rn
                   order by t.rn
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


set @query 
  = 'select *
     from
     (
       select userid,
         username,
         value,
         col + ''_''+ cast(rn as varchar(10)) col
       from
       (
         select u.userid,
           u.username,
           p.permissionid,
           p.rights,
           row_number() over(partition by u.userid 
                             order by p.permissionid, p.rights) rn
         from table_user u
         left join Table_UserPermissions p
           on u.userid = p.userid
       ) src
       unpivot
       (
         value
         for col in (permissionid, rights)
       ) unpiv
     ) x1
     pivot
     (
       max(value)
       for col in ('+ @colspivot +')
     ) p
     order by userid'

exec(@query)

См. SQL Fiddle с демонстрацией

Результат для обоих:

| USERID | USERNAME | PERMISSIONID_1 | RIGHTS_1 | PERMISSIONID_2 | RIGHTS_2 | PERMISSIONID_3 | RIGHTS_3 |
---------------------------------------------------------------------------------------------------------
|      1 |     John |             10 |        1 |             11 |        2 |             12 |        3 |
|      2 |      Joe |             11 |        2 |             12 |        3 |         (null) |   (null) |
|      3 |    James |             10 |        2 |         (null) |   (null) |         (null) |   (null) |
0 голосов
/ 18 ноября 2008

Возможно, вы захотите взглянуть на следующий пример создания перекрестных запросов в SQL:

http://www.databasejournal.com/features/mssql/article.php/3521101/Cross-Tab-reports-in-SQL-Server-2005.htm

Похоже, есть новые операции, которые были включены как часть SQL Server 2005, называемые PIVOT и UNPIVOT

0 голосов
/ 18 ноября 2008

Похоже, что сводная функция была разработана для ситуаций, когда вы можете использовать агрегатную функцию в одном из полей. Например, если бы я хотел знать, сколько дохода каждый продавец делал для компании x. Я мог бы подвести итог ценового поля из таблицы продаж. Затем я получу информацию о продавце и о том, сколько у него выручки. Для разрешений, однако, нет смысла суммировать / считать / и т.д. в поле allowId или поле Rights.

0 голосов
/ 18 ноября 2008

Если это приемлемо, стратегия, которую я использовал как для проектирования, так и для реализации, заключается в том, чтобы выгружать запрос без привязки в Excel или Access. Оба имеют более дружественные пользовательские интерфейсы для поворота данных, и гораздо больше людей чувствуют себя комфортно в этой среде.

Если у вас есть понравившийся дизайн, вам будет проще подумать о том, как продублировать его в TSQL.

0 голосов
/ 18 ноября 2008

Вы можете создать временную таблицу_flatuserpermissions:

UserID
PermissionID1
Rights1
PermissionID2
Rights2
...etc to as many permission/right combinations as you need

Вставить записи в эту таблицу из Table_user со всеми полями прав доступа и прав.

Обновите записи в этой таблице из table_userpermissions - вставьте первую запись и установите PermissionID1 & Rights1, Вторая запись для обновления пользователя PermissionsID2 & Rights2 и т. Д.

Затем вы запрашиваете эту таблицу для генерации вашего отчета.

Лично я бы просто придерживался столбцов UserId, UserName, PermissionID, Rights, которые у вас сейчас есть.

Возможно заменить в некотором тексте PermissionID и Права вместо числовых значений.

Может быть, отсортировать таблицу по PermissionID, User вместо User, PermissionID, чтобы аудитор мог проверять пользователей по каждому типу разрешений.

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