2008-11-18 2 views
6

Я работаю с существующей базой данных и пытаюсь написать запрос sql, чтобы получить всю информацию об учетной записи, включая уровни разрешений. Это для аудита безопасности. Мы хотим с легкостью сбрасывать всю эту информацию, чтобы было легко сравнивать ее. Моя проблема в том, что для разрешений есть таблица моста/ссылки, поэтому для каждого пользователя есть несколько записей. Я хочу вернуть результаты со всеми разрешениями для одного пользователя в одной строке. Вот пример:Справка по 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, что я могу это сделать?

+0

Я * знаю * Я почти наверняка видел этот вопрос на этом сайте не менее двух раз. Но я не могу его найти. – 2008-11-18 17:32:24

ответ

3

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

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; 

Вы должны явно добавить столбец аналогичный максимум (...) для каждого permissionid.

+1

Также см. Ключевое слово PIVOT для SQL Server 2005 и более поздних версий – 2008-11-18 17:50:32

0

Вы могли бы создать временные table_flatuserpermissions из:

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

Вставка записей в эту таблицу из Table_user со всем разрешением & поля прав нулевым.

Обновление записей в этой таблице из table_userpermissions - первой звукозаписывающей вставки и установить PermissionID1 & Rights1, вторая запись для пользователя обновление PermissionsID2 & правах2 и т.д.

Тогда вы запрашиваете эту таблицу, чтобы сгенерировать отчет.

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

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

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

1

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

No.

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

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

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

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

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

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

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

Спасибо. Я думаю, что это тоже не будет работать только с sql. Я не собираю вместе значения поля, как показано в большинстве примеров сводных данных, и есть слишком много полей разрешений для ввода столбцов. Администратор имеет более 120 разрешений. Я думаю, мне нужно будет с ним связать C#. – MaxGeek 2008-11-18 18:16:59

0

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

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

0

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

1

Если вы используете 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, что можно достичь того же поведения.

0

Для этого типа преобразования данных вам необходимо выполнить как 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 with Demo

Dynamic PIVOT:

Если у Вас есть неизвестное число permissionid с и 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 with demo

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

| 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) |