patternsqlModerate
Recursive CTE to find Total for all children
Viewed 0 times
totalchildrenallcterecursiveforfind
Problem
Here is an assembly tree that I want to search using a recursive
Meaning if I search for 'Rivet', I want to know the total count at each level within the assembly, not just the direct children count.
Currently, I can get the direct parents, but want to know how to extend my CTE to allow me to roll this information upward.
Creation script
T-SQL Query (presumably CTE) with the expected results below. I want to know the total amount per assembly given any part. Meaning if I search for 'Rivet', I want to know the total count at each level within the assembly, not just the direct children count.
Assembly (id:1)
|
|-Rivet
|-Rivet
|-SubAssembly (id:2)
| |
| |-Rivet
| |-Bolt
| |-Bolt
| |-SubSubAssembly (id:3)
| |
| |-Rivet
| |-Rivet
|
|-SubAssembly (id:4)
|-Rivet
|-Bolt
DESIRED Results
-------
ID, Count
1 , 6
2 , 3
3 , 2
4 , 1Currently, I can get the direct parents, but want to know how to extend my CTE to allow me to roll this information upward.
With DirectParents AS(
--initialization
Select InstanceID, ParentID
From Instances i
Where i.Part = 'Rivet'
UNION ALL
--recursive execution
Select i.InstanceID, i.ParentID
From PartInstances i INNER JOIN DirectParents p
on i.ParentID = p.InstanceID
)
select ParentID, Count(instanceid) as Totals
from DirectParents
group by InstanceID, ParentID
Results
-------
ID, Count
1 , 2
2 , 2
3 , 2
4 , 1Creation script
CREATE TABLE [dbo].[Instances] (
[InstanceID] NVARCHAR (50) NOT NULL,
[Part] NVARCHAR (50) NOT NULL,
[ParentID] NVARCHAR (50) NOT NULL, );
INSERT INTO Instances
Values
(1, 'Assembly', 0),
(50, 'Rivet', 1),
(50, 'Rivet', 1),
(2, 'SubAssembly', 1),
(50, 'Rivet', 2),
(51, 'Bolt', 2),
(51, 'Bolt', 2),
(3, 'SubSubAssembly', 2),
(50, 'Rivet', 3),
(50, 'Rivet', 3),
(4, 'SubAssembly2', 1),
(50, 'Rivet', 4),
(51, 'Bolt', 4)Solution
This recursive CTE (SQL Fiddle) should work with your sample:
Output
Note: You mentioned in comments that the question only contains a simplified sample table and real data have proper indexes and handle duplicates and data adequately.
Data used (SQL Fiddle):
WITH cte(ParentID) AS(
SELECT ParentID FROM @Instances WHERE [Part] = 'Rivet'
UNION ALL
SELECT i.ParentID FROM cte c
INNER JOIN @Instances i ON c.ParentID = i.InstanceID
WHERE i.ParentID > 0
)
SELECT ParentID, count(*)
FROM cte
GROUP BY ParentID
ORDER BY ParentID
;Output
ParentID Count
1 6
2 3
3 2
4 1Note: You mentioned in comments that the question only contains a simplified sample table and real data have proper indexes and handle duplicates and data adequately.
Data used (SQL Fiddle):
DECLARE @Instances TABLE(
[InstanceID] int NOT NULL
, [Part] NVARCHAR (50) NOT NULL
, [ParentID] int NOT NULL
);
INSERT INTO @Instances([InstanceID], [Part], [ParentID])
VALUES
(1, 'Assembly', 0)
, (50, 'Rivet', 1)
, (50, 'Rivet', 1)
, (2, 'SubAssembly', 1)
, (50, 'Rivet', 2)
, (51, 'Bolt', 2)
, (51, 'Bolt', 2)
, (3, 'SubSubAssembly', 2)
, (50, 'Rivet', 3)
, (50, 'Rivet', 3)
, (4, 'SubAssembly2', 1)
, (50, 'Rivet', 4)
, (51, 'Bolt', 4)
;Code Snippets
WITH cte(ParentID) AS(
SELECT ParentID FROM @Instances WHERE [Part] = 'Rivet'
UNION ALL
SELECT i.ParentID FROM cte c
INNER JOIN @Instances i ON c.ParentID = i.InstanceID
WHERE i.ParentID > 0
)
SELECT ParentID, count(*)
FROM cte
GROUP BY ParentID
ORDER BY ParentID
;ParentID Count
1 6
2 3
3 2
4 1DECLARE @Instances TABLE(
[InstanceID] int NOT NULL
, [Part] NVARCHAR (50) NOT NULL
, [ParentID] int NOT NULL
);
INSERT INTO @Instances([InstanceID], [Part], [ParentID])
VALUES
(1, 'Assembly', 0)
, (50, 'Rivet', 1)
, (50, 'Rivet', 1)
, (2, 'SubAssembly', 1)
, (50, 'Rivet', 2)
, (51, 'Bolt', 2)
, (51, 'Bolt', 2)
, (3, 'SubSubAssembly', 2)
, (50, 'Rivet', 3)
, (50, 'Rivet', 3)
, (4, 'SubAssembly2', 1)
, (50, 'Rivet', 4)
, (51, 'Bolt', 4)
;Context
StackExchange Database Administrators Q#138928, answer score: 17
Revisions (0)
No revisions yet.