Background
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'
Question
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:
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.