Рекурсивный CTE, чтобы найти Всего для всех детей


16

Вот дерево сборки, которое я хочу найти с помощью рекурсивного T-SQLзапроса (предположительно CTE) с ожидаемыми результатами ниже. Я хочу знать общую сумму на каждую сборку с учетом любой детали.

То есть, если я ищу «Заклепка», я хочу знать общее количество на каждом уровне в сборке, а не только прямое число детей.

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

В настоящее время я могу получить прямых родителей, но хочу знать, как расширить мой CTE, чтобы позволить мне свернуть эту информацию вверх.

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

Сценарий создания

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)

Ответы:


14

Этот рекурсивный CTE ( SQL Fiddle ) должен работать с вашим образцом:

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
;

Выход

ParentID    Count
1           6
2           3
3           2
4           1

Примечание. Вы упомянули в комментариях, что вопрос содержит только упрощенную таблицу примеров, а реальные данные имеют надлежащие индексы и адекватно обрабатывают дубликаты и данные.

Используемые данные ( 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)
;

Отличный ответ, спасибо! Есть ли простое решение сделать это рекурсивно для всех Экземпляров [Сборка, Заклепка и т. Д.]?
Greenhoorn

0

Я не уверен, что понимаю, что вы имеете в виду под "количеством" и откуда в вашем образце взяты (?) PartInstances и столбцы id и count, но я вычислил то, что я предполагаю, из ваших данных образца.

;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

Я надеюсь, что это даст вам некоторые идеи.

Обновить

Я понимаю, что это тестовый пример, но ваши данные нарушают все, начиная с 1NF. Скорее всего, ваш стол должен быть разбит на две части и нормализован.


Смотрите результаты в первом разделе кода .. те, которые желательны. При поиске «Заклепки» я ищу все заклепки, полученные с помощью любого куска дерева. Значение instanceID 1 должно дать мне результат 6. Это означает, что в его полной древовидной структуре содержится 6 заклепок.
Markokstate
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.