Рекурсивний CTE, щоб знайти Total для всіх дітей


16

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

Значить, якщо я шукаю "Rivet", я хочу знати загальний підрахунок на кожному рівні в межах зборів, а не лише прямий підрахунок дітей.

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

Я не впевнений, що я розумію, що ви маєте на увазі під "сумою" і звідки у вашій вибірці беруться ідентифікатор та кількість стовпців (?) Та стовпців (?), Але я підрахував, що я здогадуюсь із ваших даних вибірки.

;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. Найімовірніше, ваша таблиця повинна бути розбита на два і нормалізована.


Результати дивіться в першому розділі коду .. бажані. Під час пошуку "Rivet" я шукаю, щоб отримати всі заклепки, що дають будь-який шматок дерева. Значення instance 1 повинно дати мені результат 6. Значить, він містить 6 загальних заклепок у його повному складі дерева.
markokstate
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.