CTE recursivo para encontrar Total para todos los niños

16

Aquí hay un árbol de ensamblaje que quiero buscar usando una T-SQLconsulta recursiva (presumiblemente CTE) con los resultados esperados a continuación. Quiero saber la cantidad total por ensamblaje dada cualquier parte.

Es decir, si busco 'Remache', quiero saber el recuento total en cada nivel dentro de la asamblea, no solo el recuento directo de niños.

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

Actualmente, puedo obtener los padres directos, pero quiero saber cómo extender mi CTE para permitirme transferir esta información hacia arriba.

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

Guion de creacion

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
fuente

Respuestas:

14

Este CTE recursivo ( SQL Fiddle ) debería funcionar con su muestra:

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
;

Salida

ParentID    Count
1           6
2           3
3           2
4           1

Nota: Usted mencionó en los comentarios que la pregunta solo contiene una tabla de muestra simplificada y que los datos reales tienen índices adecuados y manejan los duplicados y los datos adecuadamente.

Datos utilizados ( 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
fuente
Gran respuesta gracias! ¿Existe una solución fácil para hacer esto de forma recursiva para todas las instancias [Ensamblaje, Remache, etc.]?
greenhoorn
0

No estoy seguro de entender lo que quiere decir con "cantidad" y de dónde provienen las identificaciones y el recuento de PartInstances y columnas de la tabla en su muestra, pero calculé lo que supongo a partir de sus datos de muestra.

;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

Espero que esto te dé algunas ideas.

Actualizar

Entiendo que este es un ejemplo de prueba, pero sus datos rompen todo a partir de 1NF. Lo más probable es que su mesa se divida en dos y se normalice.

Alex Kudryashev
fuente
Vea los resultados en la primera sección del código ... esos son deseados. Cuando busco 'Remache', busco obtener un total de todos los remaches dados cualquier pieza en el árbol. Es decir, el Id. De instancia 1 debería darme un resultado de 6. Lo que significa que contiene 6 remaches totales en su estructura de árbol completa.
markokstate