Rekursiver CTE, um die Summe für alle Kinder zu finden

16

Hier ist ein Assemblybaum, den ich mithilfe einer rekursiven T-SQLAbfrage (vermutlich CTE) mit den erwarteten Ergebnissen unten durchsuchen möchte . Ich möchte den Gesamtbetrag pro Baugruppe für ein Teil erfahren.

Das heißt, wenn ich nach "Rivet" suche, möchte ich die Gesamtzahl auf jeder Ebene in der Baugruppe wissen, nicht nur die Anzahl der direkten Kinder.

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 , 1

Derzeit kann ich die direkten Eltern erreichen, möchte aber wissen, wie ich meinen CTE erweitern kann, damit ich diese Informationen nach oben rollen kann.

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 , 1

Erstellungsskript

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)
markokstate
quelle

Antworten:

14

Dieser rekursive CTE ( SQL Fiddle ) sollte mit Ihrem Beispiel funktionieren:

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
;

Ausgabe

ParentID    Count
1           6
2           3
3           2
4           1

Hinweis: Sie haben in den Kommentaren erwähnt, dass die Frage nur eine vereinfachte Beispieltabelle enthält und die realen Daten über geeignete Indizes verfügen und mit Duplikaten und Daten angemessen umgehen.

Verwendete Daten ( 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)
;
Julien Vavasseur
quelle
Tolle Antwort, danke! Gibt es eine einfache Lösung, um dies rekursiv für alle Instanzen [Assembly, Rivet usw.] zu tun?
Greenhoorn
0

Ich bin nicht sicher, ob ich verstehe, was Sie unter "Betrag" verstehen und woher die Tabelle (?) PartInstances und Spalten id und count in Ihrer Stichprobe stammen, aber ich habe berechnet, was ich aus Ihren Stichprobendaten erraten habe.

;with ins as (
select [InstanceID], [Part],[ParentID],0 lvl
from instances where ParentID=0
union all
select i.[InstanceID], i.[Part],i.[ParentID], lvl+1
from instances i 
inner join ins on i.parentid=ins.InstanceID
)
select InstanceID,part,COUNT(*) cnt
from ins
group by instanceid,part

Ich hoffe, das wird Ihnen einige Ideen geben.

Aktualisieren

Ich verstehe, dass dies ein Testbeispiel ist, aber Ihre Daten brechen alles ab 1NF. Höchstwahrscheinlich sollte Ihr Tisch in zwei Teile geteilt und normalisiert werden.

Alex Kudryashev
quelle
Sehen Sie sich die Ergebnisse im ersten Codeabschnitt an. Diese sind erwünscht. Bei der Suche nach "Rivet" versuche ich, eine Summe aller Nieten zu erhalten, die mit einem Stück im Baum versehen sind. Die Instanz-ID 1 sollte ein Ergebnis von 6 ergeben. Das heißt, sie enthält insgesamt 6 Nieten in ihrer vollständigen Baumstruktur.
Markokstate