I've got a group table, users table, and a group_members table.
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 }
My requirements state that a group can have other groups as members. The user needs to be considered a member of all groups that any group they are a member of.
For Example, consider the following data in these tables:
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' | |
My desired result of asking the question What groups is 'USER' a member of? should be
member
==============
'SYSTEM_ADMIN'
'OE_ADMIN'
'AR_ADMIN'
I've got the following query built and providing me with the required results, but it looks a little complex.
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')
Any suggestions on how to make this query simpler, or less cluttered?
Your recursive CTE looks fine.
How does it perform?
I don't think there's any significant way to make it a lot simpler - the recursive CTEs always look kind of messy.
The WHERE IN is equivalent to a JOIN, but I don't see it as being more readable and the execution plan should be pretty equivalent.
Your root row could be represented as a CTE before the recursive one, but it won't really save much in readability:
WITH root AS ()
,CTE AS ()
SELECT FROM ROOT
UNION
SELECT FROM CTE
In any case, you could put this in a view or inline table-valued-function (with a parameter) to make it easy to use from elsewhere in the system so you don't have to see it very much.
Something I've done for hierarchies is to build a more high-performance flattened/denormalized version and update it on a trigger or timer and use that as a read-only performance enhancer. For instance, in a technical support system we build where a question could be tagged in a hierarchy, we filled in all the parents (printers->hp->laserjet) when a child was selected, so it was easy to query for problems with hp printers on any version of Windows or problems with laserjets on Windows XP.
Based on @Cade Roux's answer, further reflection guided me to the following Stored Procedure
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
This simplifies the original query, and properly fulfills my requirement to get back a list of group names that @user_id is a member of, as well as the names of the groups that are members of the top-level group.
This code only allows groups in groups to one level.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.