CTE Рекурсия с использованием группы на определенной глубине - PullRequest
0 голосов
/ 02 мая 2018

У меня есть таблица, представляющая структуру папок - (т.е. рекурсия по id и родительскому полю) - эти данные на самом деле представляют элементы в структуре папки (поле элемента просто есть для примера, однако мой случай будет объединением с много предметов. Я создал строку пути (не нужно использовать) - эта строка представляет путь каждой записи в наборе данных (поле ветви)

мой запрос должен фильтроваться по всем записям глубиной более 3-х уровней, а затем подсчитывать любые элементы на 3-х уровнях или ниже в каждой ветви рекурсии (в моих данных это будет означать, что записи 5 и 6 представляют узлы верхнего уровня и будут записи, используемые для группировки.

Любая помощь?

DECLARE @tbl TABLE
  ( 
   Id int
  ,ParentId int
  ,branch varchar(100)
  ,depth int
  ,item varchar(20)
  )
INSERT  INTO @tbl
        ( Id, ParentId,item )
VALUES 
(1, NULL,Null),
(2, 1,Null),
(3, 1,Null),
(4, 3,Null),
(5, 4,Null),
(6, 5,'car'),
(7, 6,'bus'),
(8, 7,'truck'),
(9, 8,'car'),
(10,8,'bike'),
(11,5,'car'),
(12,5,'truck'),
(13,4,'truck'),
(14,8,'bike'),
(15,8,'bus');

--select t_package.package_id, t_package.parent_ID from t_package 

;WITH abcd
        AS (
              -- anchor
            SELECT   id
                    ,ParentID
                    ,CAST(id AS VARCHAR(100)) AS [Path]
                    ,0 as depth
                    ,item
            FROM    @tbl
            WHERE   ParentId is Null
            UNION ALL
              --recursive member
            SELECT  t.id
                   ,t.ParentID
                   ,CAST(a.[Path] + ',' + CAST( t.ID AS VARCHAR(100)) AS varchar(100)) AS [Path]
                   ,a.depth +1
                   ,t.item
            FROM    @tbl AS t
                    JOIN abcd AS a ON t.ParentId = a.id
           )
insert into @tbl (id,parentID,branch,depth,item) select * from abcd

select * from @tbl 
where branch is not null

Это будет означать, что если вы сгруппировались на уровне 3 и требовали подсчета предметов на каждом уровне, ваш набор результатов будет выглядеть примерно так:

ID     Depth  -- car -- bike -- bus -- truck
5      3      --  3  --  2   -- 2   --  2  
13     3      --  0  --  0   -- 0   --  1

Ответы [ 2 ]

0 голосов
/ 03 мая 2018

Вы также можете начать рекурсивный CTE на уровне 3.
Затем развернитесь для предметов.

Пример кода:

DECLARE @tbl TABLE(ID INT, ParentID INT, Item VARCHAR(20));
INSERT INTO @tbl (ID, ParentID, Item) VALUES
                            (1,  NULL, NULL),
                        (2,  1, NULL),
                        (3,  1, NULL),
                    (4,  3, NULL),
                (5,  4, NULL),
            (6,  5, 'car'),
        (7,  6, 'bus'),
    (8,  7, 'truck'),
(9,  8, 'car'),
(10, 8, 'bike'),
            (11, 5, 'car'),
            (12, 5, 'truck'),
                 (13, 4, 'truck'),
(14, 8, 'bike'),
(15, 8, 'bus');

;with CTE as (
    select t0.ID as tier0id, t1.ID as tier1id, t2.ID as tier2id, t3.ID as tier3id, 0 as lvl, t3.ID, t3.ParentID, t3.Item
    from @tbl t0
    join @tbl t1 on (t0.ParentID is null and t1.ParentID = t0.ID)
    join @tbl t2 on (t2.ParentID = t1.ID)
    join @tbl t3 on (t3.ParentID = t2.ID)

    union all

    select tier0id, tier1id, tier2id, tier3id, lvl + 1, t.ID, t.ParentID, t.Item
    from CTE
    join @tbl t on (t.ParentID = CTE.ID)
)
select *
from (
    select distinct tier3id as ID, 3 as Depth, ID as ChildId, Item 
    from CTE
) q
pivot (
    count(ChildId) 
    for Item in ([car], [bike], [bus], [truck])
) pvt
order by ID;
0 голосов
/ 03 мая 2018

Это было довольно интригующе, поэтому мне пришлось поделиться своим предыдущим опытом с CTE.

Ниже приведен запрос, который должен завершить вашу цель. Имейте в виду, однако, что так, как вы дали желаемый результат, вы всегда будете в конечном итоге жестко кодировать значения Item в своем запросе. Скорее всего, если результирующий домен значений для Item меняется, вам придется подумать о написании динамического SQL. В приведенном ниже решении я использую «условную» агрегацию, которая, как было доказано, не является «нарушителем производительности». Я уверен, что другой подход может быть введен с синтаксисом PIVOT. Производительность, следует дополнительно проанализировать и, возможно, ввести некоторый сценарий среднего индексации.

Так или иначе, это должно сработать, и, надеюсь, это принесет вам некоторую пользу:

DECLARE @summaryDepth INT = 3;

DECLARE @tbl TABLE( 
    ID INT, ParentID INT, Item VARCHAR(20));
INSERT @tbl (
    ID, ParentID, Item)
VALUES
    (1,  NULL, NULL),
    (2,  1, NULL),
    (3,  1, NULL),
    (4,  3, NULL),
    (5,  4, NULL),
    (6,  5, 'car'),
    (7,  6, 'bus'),
    (8,  7, 'truck'),
    (9,  8, 'car'),
    (10, 8, 'bike'),
    (11, 5, 'car'),
    (12, 5, 'truck'),
    (13, 4, 'truck'),
    (14, 8, 'bike'),
    (15, 8, 'bus');

/*
(1, NULL, NULL),*/
--(2, 1, NULL),
--(3, 1, NULL),
----(4, 3, NULL),
------(13,4,'truck'),
------(5, 4, NULL),
--------(6, 5,'car'),
--------(11,5,'car'),
--------(12,5,'truck'),
----------(7, 6,'bus'),
------------(8, 7,'truck'),
--------------(9, 8,'car'),
--------------(10,8,'bike'),
--------------(14,8,'bike'),
--------------(15,8,'bus');

;WITH CTE_Hierarchy AS (
    SELECT 
        ID, ParentID, Item,
        CAST(ID AS VARCHAR(100)) AS [Path],
        0 [Depth],
        CASE 
            WHEN @summaryDepth = 0 THEN ID 
            ELSE NULL 
        END [SummaryDepthRootID]
    FROM @tbl
    WHERE ParentId IS NULL

    UNION ALL

    SELECT
        child.ID, child.ParentID, child.Item,
        CAST(parent.[Path] + '/' + CAST(child.ID AS VARCHAR(100)) AS VARCHAR(100)) [Path],
        parent.[Depth] + 1 [Depth],
        CASE 
            WHEN parent.SummaryDepthRootID IS NOT NULL THEN parent.SummaryDepthRootID
            WHEN @summaryDepth = (parent.[Depth] + 1) THEN child.ID 
            ELSE NULL 
        END [SummaryDepthRootID]
    FROM @tbl AS child
    JOIN CTE_Hierarchy AS parent ON parent.ID = child.ParentID
)
SELECT 
    SummaryDepthRootID [ID],
    @summaryDepth [Depth],
    COUNT (CASE WHEN Item='car' THEN 1 ELSE NULL END) [car],
    COUNT (CASE WHEN Item='bike' THEN 1 ELSE NULL END) [bike],
    COUNT (CASE WHEN Item='bus' THEN 1 ELSE NULL END) [bus],
    COUNT (CASE WHEN Item='truck' THEN 1 ELSE NULL END) [truck]
FROM CTE_Hierarchy
WHERE SummaryDepthRootID IS NOT NULL
GROUP BY SummaryDepthRootID;
GO
...