CTE récursif pour trouver Total pour tous les enfants

16

Voici un arbre d'assemblage que je souhaite rechercher à l'aide d'une T-SQLrequête récursive (probablement CTE) avec les résultats attendus ci-dessous. Je veux connaître le montant total par assemblage pour chaque pièce.

Ce qui signifie que si je recherche «Rivet», je veux connaître le nombre total à chaque niveau de l'assemblage, pas seulement le nombre direct d'enfants.

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

Actuellement, je peux obtenir les parents directs, mais je veux savoir comment étendre mon CTE pour me permettre de rouler ces informations vers le haut.

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

Script de création

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
la source

Réponses:

14

Ce CTE récursif ( SQL Fiddle ) devrait fonctionner avec votre exemple:

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
;

Production

ParentID    Count
1           6
2           3
3           2
4           1

Remarque: Vous avez mentionné dans les commentaires que la question ne contient qu'un exemple de table simplifiée et que les données réelles ont des index appropriés et gèrent correctement les doublons et les données.

Données utilisées ( 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
la source
Grande réponse merci! Existe-t-il une solution simple pour le faire de manière récursive pour toutes les instances [Assembly, Rivet etc.]?
greenhoorn
0

Je ne suis pas sûr de comprendre ce que vous entendez par "montant" et d'où proviennent les identifiants et le nombre de tables (?) PartInstances et colonnes dans votre échantillon, mais j'ai calculé ce que je suppose à partir de vos données d'échantillon.

;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

J'espère que cela vous donnera quelques idées.

Mise à jour

Je comprends qu'il s'agit d'un exemple de test, mais vos données cassent tout à partir de 1NF. Votre table devrait très probablement être brisée en deux et normalisée.

Alex Kudryashev
la source
Voir les résultats dans la première section de code .. ceux-ci sont souhaités. Lors de la recherche de «Rivet», je cherche à obtenir un total de tous les rivets étant donné n'importe quelle pièce de l'arbre. Signification instanceID 1 devrait me donner un résultat de 6. Cela signifie qu'il contient 6 rivets au total dans sa structure arborescente complète.
markokstate