【问题标题】:SQL query help with bridge table桥接表的 SQL 查询帮助
【发布时间】:2008-11-18 17:13:03
【问题描述】:

我正在使用现有数据库并尝试编写一个 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 链接到带有权限名称及其作用的表。 Right 就像 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 的方法可以做到这一点?

【问题讨论】:

  • 知道我在这个网站上几乎完全看到过这个问题至少两次。但我找不到它。

标签: sql sql-server pivot


【解决方案1】:

你可以这样做:

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;

您必须为每个权限 ID 显式添加类似的 max(...) 列。

【讨论】:

  • 另见 sql server 2005 及更高版本的 PIVOT 关键字
【解决方案2】:

如果你使用 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”,发现有几个不同的 MSSQL 存储过程 (I)、(II) 可以实现相同的行为。

【讨论】:

    【解决方案3】:

    简答:

    没有。

    您不能在查询中动态添加列。

    请记住,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)
    

    【讨论】:

    • 谢谢。我认为它也不适用于 sql。我没有像大多数数据透视示例显示的那样将字段的值加在一起,并且有太多权限字段供我编写列。管理员拥有超过 120 个权限。我想我必须用 C# 编写代码。
    【解决方案4】:

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

    用户ID
    权限 ID1
    权利1
    权限 ID2
    权利2
    ...等您需要的尽可能多的权限/权利组合

    从 Table_user 将记录插入此表,所有权限和权限字段均为空。

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

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

    就个人而言,我会坚持使用您现在拥有的 UserId、UserName、PermissionID、Rights 列。

    可以在某些文本中替换 PermissionID 和 Rights,而不是数值。

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

    【讨论】:

      【解决方案5】:

      如果可以接受,我用于设计和/或实施的策略是将查询转储到 Excel 或 Access 中。两者都具有更友好的数据透视用户界面,并且更多人在这种环境中感到舒适。

      一旦有了自己喜欢的设计,就更容易考虑如何在 TSQL 中复制它。

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          【解决方案7】:

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

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

          SQL Server 2005 中似乎包含了一些新操作,称为 PIVOT 和 UNPIVOT

          【讨论】:

            【解决方案8】:

            对于这种类型的数据转换,您需要对数据执行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)
            ;
            

            静态枢轴:

            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

            动态枢轴:

            如果permissionids和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) |
            

            【讨论】:

              猜你喜欢
              • 2011-09-06
              • 1970-01-01
              • 1970-01-01
              • 2010-10-20
              • 1970-01-01
              • 1970-01-01
              • 2011-09-19
              相关资源
              最近更新 更多