I have a query like the simplified version below, which produces the results table below it.
In this case, the data consists of two tree structures of three levels each.
What I need, for each tree ('root'), is each child along with all the parents which include it and all the parents which exclude it.
The query works--but the execution time is extremely slow due to the cross apply method used.
Is there another way to do this that might be faster?
DECLARE @Data TABLE (Rt VARCHAR(MAX), Parent VARCHAR(MAX), Child VARCHAR(MAX))
INSERT INTO @Data (Rt, Parent, Child) VALUES
('root1', 'parent1', 'child1'),
('root1', 'parent1', 'child2'),
('root1', 'parent2', 'child1'),
('root1', 'parent2', 'child2'),
('root1', 'parent2', 'child3'),
('root1', 'parent3', 'child2'),
('root1', 'parent3', 'child3'),
('root1', 'parent4', 'child4'),
('root1', 'parent5', 'child2'),
('root2', 'parent6', 'child1'),
('root2', 'parent7', 'child5')
SELECT
D.Rt 'Root'
, D.Child
, STRING_AGG(D.Parent, ', ') 'In-Parents'
, STRING_AGG(xP.Parent, ', ') 'Ex-Parents'
FROM @Data D
CROSS APPLY (
SELECT
Di.Parent
FROM @Data Di
WHERE Di.Rt = D.Rt
EXCEPT
SELECT
Dii.Parent
FROM @Data Dii
WHERE
Dii.Rt = D.Rt
AND Dii.Child = D.Child
) AS xP
GROUP BY
D.Rt
, D.Child
| Root | Child | In-Parents | Ex-Parents |
|---|---|---|---|
| root1 | child1 | parent1, parent2 | parent3, parent4, parent5 |
| child2 | parent1, parent2, parent3, parent5 | parent4 | |
| child3 | parent2, parent3 | parent1, parent4, parent5 | |
| child4 | parent4 | parent1, parent2, parent3, parent5 | |
| root2 | child1 | parent6 | parent7 |
| child5 | parent7 | parent6 |
In case the code above doesn't work (I don't have access to STRING_AGG), here is what I'm actually using to get these results:
DECLARE @Data TABLE (Rt VARCHAR(MAX), Parent VARCHAR(MAX), Child VARCHAR(MAX))
INSERT INTO @Data (Rt, Parent, Child) VALUES
('root1', 'parent1', 'child1'),
('root1', 'parent1', 'child2'),
('root1', 'parent2', 'child1'),
('root1', 'parent2', 'child2'),
('root1', 'parent2', 'child3'),
('root1', 'parent3', 'child2'),
('root1', 'parent3', 'child3'),
('root1', 'parent4', 'child4'),
('root1', 'parent5', 'child2'),
('root2', 'parent6', 'child1'),
('root2', 'parent7', 'child5')
; WITH Res AS (
SELECT
D.Rt
, D.Child
, D.Parent 'InParent'
, xP.Parent 'ExParent'
FROM @Data D
CROSS APPLY (
SELECT
Di.Parent
FROM @Data Di
WHERE Di.Rt = D.Rt
EXCEPT
SELECT
Dii.Parent
FROM @Data Dii
WHERE
Dii.Rt = D.Rt
AND Dii.Child = D.Child
) AS xP
)
SELECT
IIF(R.Rt = LAG(R.Rt, 1) OVER(ORDER BY R.Rt, R.Child), '', R.Rt) 'Root'
, R.Child
, STUFF((
SELECT ', ' + R2.InParent
FROM Res R2
WHERE
R2.Rt = R.Rt
AND R2.Child = R.Child
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '') 'In-Parents'
, STUFF((
SELECT ', ' + R2.ExParent
FROM Res R2
WHERE
R2.Rt = R.Rt
AND R2.Child = R.Child
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '') 'Ex-Parents'
FROM Res R
GROUP BY
R.Rt
, R.Child
ORDER BY
R.Rt
, R.Child