If there is a table-tree, for example:
CREATE TABLE tree (ID int not NULL, ParentID int NULL, PRIMARY KEY (ID)); INSERT INTO tree (ID, ParentID) VALUES (1, NULL), (2, 1), (4, 2), (5, 2), (6, 2), (3, 1), (7, 3), (8, 3), (9, 3), (10, 3);
and when working with it there are many places that require a recursive code, then for convenience I recommend having two table functions.
Something like
CREATE FUNCTION treeItemAncestors ( @itemID int ) RETURNS TABLE AS RETURN WITH cte(ID, ParentID) AS ( SELECT ID, ParentID FROM tree WHERE ID = @itemID UNION ALL SELECT t.ID, t.ParentID FROM tree t JOIN cte ON cte.ParentID = t.ID ) SELECT ID FROM cte GO
and
CREATE FUNCTION treeItemDescendants ( @itemID int ) RETURNS TABLE AS RETURN WITH cte(ParentID, ID) AS ( SELECT ParentID, ID FROM tree WHERE ID = @itemID UNION ALL SELECT t.ParentID, t.ID FROM tree t JOIN cte ON cte.ID = t.ParentID ) SELECT ID FROM cte GO
I called them descendants and ancestors , but they also return the node itself, which is passed as a parameter (based on my practice, most often this is what is required, but if necessary, you can exclude it). Also, they only return an ID list from me. If you need other columns, then you need to add them to the cte and the resulting select .
In order for treeItemAncestors to treeItemAncestors well in this case, the existing clustered primary key is sufficient.
For good work treeItemDescendants it is desirable to add an index on the ParentID :
CREATE INDEX ix_tree_ParentID ON tree (ParentID) INCLUDE (ID);
Other fields, if any, are necessary, can also be added to include .
Then you can work with them, for example:
SELECT ID FROM treeItemAncestors(10);
or
SELECT DISTINCT a.ID FROM (VALUES (6), (9)) idlist(id) CROSS APPLY treeItemAncestors(idlist.id) a;
or
DELETE t FROM tree t join treeItemDescendants(3) d on d.ID = t.ID;
etc.