Rekurencyjne CTE, aby znaleźć sumę dla wszystkich dzieci

16

Oto drzewo asemblera, które chcę wyszukać za pomocą T-SQLzapytania rekurencyjnego (prawdopodobnie CTE) z oczekiwanymi wynikami poniżej. Chcę poznać całkowitą kwotę na zespół danego elementu.

To znaczy, jeśli szukam „Nit”, chcę znać łączną liczbę na każdym poziomie w zespole, a nie tylko bezpośrednie dzieci.

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

Obecnie mogę uzyskać bezpośrednich rodziców, ale chcę wiedzieć, jak rozszerzyć moje CTE, aby umożliwić mi zwiększenie tych informacji w górę.

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

Skrypt tworzenia

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
źródło

Odpowiedzi:

14

Ta rekurencyjna CTE ( skrzypce SQL ) powinna działać z twoją próbką:

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
;

Wynik

ParentID    Count
1           6
2           3
3           2
4           1

Uwaga: Wspomniałeś w komentarzach, że pytanie zawiera tylko uproszczoną tabelę przykładową, a rzeczywiste dane mają właściwe indeksy i odpowiednio obsługują duplikaty i dane.

Wykorzystane dane ( skrzypce SQL ):

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
źródło
Świetna odpowiedź, dziękuję! Czy istnieje łatwe rozwiązanie tego problemu rekurencyjnie dla wszystkich instancji [montaż, nit itp.]?
greenhoorn
0

Nie jestem pewien, czy rozumiem, co masz na myśli przez „ilość” i skąd w tabeli (?) Identyfikator i liczba składników PartInstances i kolumn pochodzą z Twojej próbki, ale obliczyłem to, co sądzę na podstawie danych z próbki.

;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

Mam nadzieję, że da ci to kilka pomysłów.

Aktualizacja

Rozumiem, że to jest przykład testowy, ale twoje dane psują wszystko, zaczynając od 1NF. Najprawdopodobniej twój stół powinien zostać podzielony na dwie części i znormalizowany.

Alex Kudryashev
źródło
Zobacz wyniki w pierwszej sekcji kodu. Te są pożądane. Szukając „Nitu”, chcę uzyskać w sumie wszystkie nity podane w dowolnym kawałku drzewa. Oznacza to, że instanceID 1 powinien dać wynik 6. Oznacza to, że zawiera 6 nitów w swojej pełnej strukturze drzewa.
markokstate