it-swarm.com.de

Rekursiver CTE, um Total für alle Kinder zu finden

Hier ist ein Assembly-Baum, den ich mit einer rekursiven T-SQL Abfrage (vermutlich CTE) mit den erwarteten Ergebnissen unten durchsuchen möchte. Ich möchte den Gesamtbetrag pro Versammlung für einen Teil wissen.

Das heißt, wenn ich nach "Niet" suche, möchte ich die Gesamtzahl auf jeder Ebene innerhalb der Versammlung 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)
16
markokstate

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 Kommentaren erwähnt, dass die Frage nur eine vereinfachte Beispieltabelle enthält und reale Daten über geeignete Indizes verfügen und Duplikate und Daten angemessen behandeln.

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)
;
14

Ich bin nicht sicher, ob ich verstehe, was Sie unter "Betrag" verstehen und woher Tabelle (?) PartInstances und Spalten id und count von in kommen Ihre Stichprobe, aber ich habe berechnet, was ich aus Ihren Beispieldaten schätze.

;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 gibt Ihnen einige Ideen.

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.

0
Alex Kudryashev