SQL查询帮助桥表

时间:2008-11-18 17:13:04

标签: sql sql-server pivot

我正在使用现有数据库并尝试编写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链接到具有Permission名称及其功能的表。右边就像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

理想情况下,我希望所有用户都这样做。 我发现最接近的是SQL Server 2005中的Pivot函数。 http://geekswithblogs.net/lorint/archive/2006/08/04/87166.aspx 我可以告诉我的问题是,我需要为每个用户命名每一列,但我不确定如何获得权限级别。使用真实数据,我有大约130个用户和40个不同的权限。

我可以用sql做另一种方法吗?

8 个答案:

答案 0 :(得分: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明确添加类似的max(...)列。

答案 1 :(得分:1)

简短回答:

没有

您无法在查询中动态添加列。

请记住,SQL是一种基于集合的语言。您可以一起查询集合和连接集。

你要挖掘的是一个递归列表,要求列表水平而不是垂直排列。

你可以使用一组自联接来排序,伪造它,但为了做到这一点,你必须在编写查询之前知道所有可能的权限......这是其他建议所提出的。< / p>

您还可以将记录集拉回另一种语言,然后迭代它以生成正确的列。

类似的东西:

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)

答案 2 :(得分: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

对'mssql group_concat'的快速谷歌搜索显示了两个不同的存储过程(I),(II),可以实现相同的行为。

答案 3 :(得分:0)

您可以创建一个临时的table_flatuserpermissions:

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

从Table_user向此表插入记录,并获得所有权限&amp;权利领域无效。

从table_userpermissions更新此表上的记录 - 第一个记录插入并设置PermissionID1&amp; Rights1,用户更新的第二条记录PermissionsID2&amp; Rights2等。

然后您查询此表格以生成报告。

就个人而言,我只是坚持你现在拥有的UserId,UserName,PermissionID,Rights列。

也许在某些文本中替换PermissionID和Rights而不是数值。

可能按PermissionID,User而不是User,PermissionID对表进行排序,以便审核员可以检查每种权限类型的用户。

答案 4 :(得分:0)

如果可以接受,我用过的策略,无论是设计还是实现,都是将查询转储到Excel或Access中。两者都有更友好的用户界面来转动数据,并且在这种环境中有更多的人感到舒服。

一旦你有自己喜欢的设计,就可以更容易地思考如何在TSQL中复制它。

答案 5 :(得分:0)

似乎枢轴功能是针对您可以在其中一个字段上使用聚合函数的情况而设计的。就像我想知道每个销售人员为公司x赚取多少收入一样。我可以从销售表中总结价格字段。然后,我会得到销售人​​员以及他们的销售收入。对于权限,尽管对permissionId字段或Rights字段进行求和/计数/等没有意义。

答案 6 :(得分:0)

您可能希望查看以下有关在SQL中创建交叉表查询的示例:

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

看起来有一些新操作包含在SQL Server 2005中,称为PIVOT和UNPIVOT

答案 7 :(得分:0)

对于此类数据转换,您需要同时执行UNPIVOTPIVOT数据。如果您知道要转换的值,则可以使用静态数据透视表对查询进行硬编码,否则可以使用动态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

动态PIVOT:

如果您的permissionidrights数量未知,那么您可以使用动态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) |