I need to write a query for a database structure where a parent element has 0-many child elements, but a child element can "have" many parents.
Note the problem I'm not trying to solve is that where the child element definition contains its parent element, and you can simply write a recursive CTE that "starts" at a child and joins back up itself until it hits a root element (ie, an element with NULL ParentID).
For this I need specifically to start at a parent and work my way down finding all children, grandchildren etc. So my database structure currently is as follows:
create table Element (
Id int identity(1, 1) not null,
Name varchar(100) not null,
-- other stuff ...
)
create table ElementRelation (
ParentElementId int not null, -- foreign key to Element.Id
ChildElementId int not null, -- foreign key to Element.Id
-- other stuff...
)
select * from Element
/* returns
Id | Name
------|---------
1 | ElementA
2 | ElementB
3 | ElementC
4 | ElementD
*/
select * from ElementRelation
/* returns
ParentElementId | ChildElementId
----------------|---------------
1 | 2
1 | 3
1 | 4
2 | 3
2 | 4
3 | 4
*/
Which results in this tree structure (pardon the cruse Paint doodle):
So you can see the typical solution of having a leaf-first table with a ParentId foreign key column doesn't work - element 4 has three immediate parents, element 3 has two, etc. It would be inappropriate for the child to declare its parent elements.
What I effectively need is a query that, given a starting parent element, finds all its immediate children. And then for each of those children, finds all of their immediate children, and so on until all paths have reached a leaf node. In this example data, a query against element 1 in this example would return the {1,2}, {1,3}, {1,4}, {2,3}, {2,4}, {3,4}, {3,4}
(though it doesn't matter whether or not the query only returns a distinct list), and a query against element 2 would return {2,4}, {2,3}, {3,4}
I could solve this with a cursor, but if there's a faster set-based means that would be preferred. If there's a better approach that would redefine the fundamental structure, that's also acceptable.
In terms of "what have you tried?" - several variants on a CTE query based on child-to-parent recursion, none of which came close to solving the problem, so I won't share them here.