我有一个组表,用户表和一个 group_members 表。
groups { group_ varchar(50) not null, etc... }
users { user_id varchar(50) not null, etc... }
group_members { group_ varchar(50 not null, member varchar(50) not null }
我的要求声明一个群组可以将其他群组作为成员。用户需要被视为他们所属的任何组的所有组的成员 例如,请考虑以下表中的以下数据:
group_members | groups | users |
========================== | ============== | ======== |
group_ member | group_ | user_id |
-------------------------- | -------------- | -------- |
'SYSTEM_ADMIN' 'OE_ADMIN' | 'SYSTEM_ADMIN' | 'USER |
'SYSTEM_ADMIN' 'AR_ADMIN' | 'OE_ADMIN' | |
'SYSTEM_ADMIN' 'USER' | 'AR_ADMIN' | |
我想问这个问题的结果 “用户”是哪个群组的成员?应为
member
==============
'SYSTEM_ADMIN'
'OE_ADMIN'
'AR_ADMIN'
我已经构建了以下查询并向我提供了所需的结果,但它看起来有点复杂。
WITH GM
AS (
SELECT GROUP_, MEMBER FROM group_members
WHERE member IN (SELECT group_ FROM groups)
)
SELECT group_ FROM group_members WHERE member = 'USER'
UNION
SELECT MEMBER AS GROUP_ FROM GM
WHERE group_ in (SELECT group_ FROM group_members WHERE member = 'USER')
有关如何使此查询更简单或更简洁的任何建议吗?
答案 0 :(得分:0)
您的递归CTE看起来很好。
它是如何表现的?
我认为没有任何重要的方法可以让它变得更简单 - 递归的CTE总是看起来很乱。
WHERE IN等同于JOIN,但我认为它不具有可读性,执行计划应该非常相同。
您的根行可以在递归之前表示为CTE,但它在可读性方面实际上不会节省太多:
WITH root AS ()
,CTE AS ()
SELECT FROM ROOT
UNION
SELECT FROM CTE
在任何情况下,您都可以将它放在视图或内联表值函数(带参数)中,以便从系统中的其他位置轻松使用,这样您就不必非常了解它。 / p>
我为层次结构所做的一些事情是构建一个更高性能的扁平化/非规范化版本,并在触发器或计时器上更新它,并将其用作只读性能增强器。例如,在我们构建的技术支持系统中,我们可以在层次结构中标记问题,我们在选择子项时填写所有父项(printer-> hp-> laserjet),因此很容易查询任何版本的Windows上的hp打印机问题或Windows XP上的laserjets问题。
答案 1 :(得分:0)
根据@Cade Roux的回答,进一步的反思引导我进入以下存储过程
ALTER PROCEDURE GetUserGroups
@user_id varchar(50)
AS
BEGIN
SET NOCOUNT ON;
SELECT group_ FROM group_members WHERE member = @user_id
UNION
SELECT MEMBER AS GROUP_ FROM group_members
WHERE group_ IN (SELECT group_ FROM group_members WHERE member = @user_id)
AND member != @user_id
END
这简化了原始查询,并正确满足了我要求获取@user_id所属的组名列表以及作为顶级组成员的组的名称。
此代码仅允许组中的组达到一个级别。