Рекурсивный CTE с древовидной иерархией SQL Server - PullRequest
0 голосов
/ 21 декабря 2018

Мне нужно использовать рекурсив с SQL Server, но я не знаю, как использовать его с моим деревом иерархии.

Мне нужна помощь для создания моего запроса и знаю, возможно ли это с помощью CTE Recursion.

Мой пример:

У меня есть две таблицы: piece (piece_id) и piece_equivalence (piece1_id, piece2_id)

Во-первых, мне нужно получить всю часть из первой таблицы:

SELECT DISTINCT p.record_id FROM piece p

Во-вторых, мне нужно проверить, есть ли кусок во второй таблице (piece1_id или piece2_id)

SELECT DISTINCT p.record_id           
        FROM piece p
        inner join piece_equivalence pe 
ON (pe.piece1_id = p.record_id OR pe.piece2_id = p.record_id)

В-третьих, если кусок существует, мне нужно проверить piece1_idили piece2_id.Этот идентификатор также может иметь эквивалентность.Поэтому я также проверю второй шаг с помощью моего piece1_id или piece2_id.

В настоящее время я выполнял рекурсивный вызов той же функции с параметром piece1 или piece2.

Графическое представление с узлами:

                                                        piece_id
                                                        ___/ \___
                                                        /         \
table : piece_equivalence               piece1_id or piece2_id    piece1_id or piece2_id
                                                /       \              /         \
table : piece_equivalence       piece1_id or piece2_id    same        same        same

Графическое изображение с буквами:

           A
       ___/ \___ ________
      /         \        \
    B            C        D
   / \          / \      / \
  D   E       F   B     E   G
 /            /
G             H

A: части B, C, D, E, F, G, H являются эквивалентами.

ПРЕДУПРЕЖДЕНИЕ: мне нужно иметь на складе всю частьс их эквивалентностью во временной таблице.Чтобы избежать повторяющихся записей или бесконечного цикла, мы должны проверить эту временную таблицу, данные существуют или нет.

РЕДАКТИРОВАТЬ:

Я сделал это:

WITH pieces_CTE 
AS
(
        SELECT TOP 1 p.record_id as parent,
              case when pe.piece1_id <> p.record_id then pe.piece1_id else pe.piece2_id end as enfant,
              1 as level
        FROM piece p
        inner join piece_equivalence pe ON (pe.piece1_id = p.record_id OR pe.piece2_id = p.record_id) AND pe.pertinence = 100
        AND pe.piece1_id <> pe.piece2_id
        UNION ALL       
        SELECT c.parent, case when enfant.piece1_id <> c.parent then enfant.piece1_id else enfant.piece2_id end as enfant,
        c.level+1 
        from pieces_CTE c
        INNER JOIN piece_equivalence enfant ON (enfant.piece1_id = c.parent OR enfant.piece2_id = c.parent)
        WHERE  enfant.pertinence = 100

)

SELECT * from pieces_CTE ORDER BY parent,level,enfant
OPTION (MAXRECURSION 32767)

Оператор прекращен.Максимальная рекурсия 100 была исчерпана до завершения оператора.

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

Но почему у меня такая же ошибка с TOP 1?

1 Ответ

0 голосов
/ 21 декабря 2018

Прежде чем начать с рекурсивным CTE, вам нужно знать кое-что

  • Вы не можете использовать DISTINCT или UNION
  • Вы не можете использовать LEFT JOIN в рекурсивной части CTE
  • Необходимо убедиться, что рекурсия не заканчивается мертвым замком.В противном случае по счетчику рекурсии по умолчанию, равному 100, CTE завершится.Пожалуйста, смотрите приведенный ниже пример:

    ОБЪЯВИТЬ @MyData TABLE (SeqNo INT IDENTITY (1,1), FullName VARCHAR (50), ManagerId INT) ВСТАВИТЬ INTO @MyData (FullName) VALUES ('CEO')

- Вставить субкомпоненты

   INSERT INTO @MyData    (
FullName,
ManagerId    )    SELECT
'Department Head 1',
ManagerId = SeqNo
FROM @MyData
    WHERE FullName = 'CEO'    UNION    SELECT
'Department Head 2',
ManagerId = SeqNo
FROM @MyData
    WHERE FullName = 'CEO'    UNION    SELECT
'Department Head 3',
ManagerId = SeqNo
FROM @MyData
    WHERE FullName = 'CEO'

   INSERT INTO @MyData    (
FullName,
ManagerId    )    SELECT
'Manager 1',
ManagerId = SeqNo
FROM @MyData
    WHERE FullName = 'Department Head 1'    UNION    SELECT
'Manager 2',
ManagerId = SeqNo
FROM @MyData
    WHERE FullName = 'Department Head 1'    UNION    SELECT
'Manager 3',
ManagerId = SeqNo
FROM @MyData
    WHERE FullName = 'Department Head 3'
   ;WITH CTE    AS    (
SELECT
    SeqNo,
    FullName,
    Manager = ISNULL(FullName,'')
    FROM @MyData
        WHERE ManagerId IS NULL
UNION ALL
SELECT
    MD.SeqNo,
    MD.FullName,
    Manager = ISNULL(CTE.FullName,'')
    FROM CTE
        INNER JOIN @MyData MD
            ON CTE.SeqNo = MD.ManagerId    )    SELECT
*
FROM CTE

SeqNo       FullName                                           Manager
----------- -------------------------------------------------- --------------------------------------------------
1           CEO                                                CEO
2           Department Head 1                                  CEO
3           Department Head 2                                  CEO
4           Department Head 3                                  CEO
7           Manager 3                                          Department Head 3
5           Manager 1                                          Department Head 1
6           Manager 2                                          Department Head 1
...