桥接表的 SQL 查询帮助
Posted
技术标签:
【中文标题】桥接表的 SQL 查询帮助【英文标题】:SQL query help with bridge table 【发布时间】: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 的方法可以做到这一点?
【问题讨论】:
我知道我在这个网站上几乎完全看到过这个问题至少两次。但我找不到它。 【参考方案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】:对于这种类型的数据转换,您需要对数据执行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)
;
静态枢轴:
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
动态枢轴:
如果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 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) |
【讨论】:
以上是关于桥接表的 SQL 查询帮助的主要内容,如果未能解决你的问题,请参考以下文章
是否可以使用 JDBC 到 ODBC 桥接进行 SQL 连接?
sql 调用从特定dnis桥接扩展 - 此特定查询为cal调用dnis,ani,扩展名,call_begin_datetime和音频