cte рекурсивная часть возвращает только одну строку - PullRequest
0 голосов
/ 09 ноября 2018

Если у меня нет соединения в рекурсивной части моего CTE, я получаю только одну строку за рекурсию, почему это так?

Код, протестированный на базе данных SQL Server 2016 и Azure SQL:

DECLARE @Number TABLE (Number INT);

INSERT INTO @Number (Number)
VALUES (1), (2);

;WITH _cte AS 
(
     SELECT Number
     FROM @Number
     UNION ALL
     SELECT _cte.Number
     FROM _cte
)
SELECT *
FROM _cte
OPTION (MAXRECURSION 2); -- just call recursive part twice to see the issue

В результате я получаю число 2 в каждой рекурсии / глубине.

Я ожидаю, что текущие строки будут повторяться в каждой рекурсии, следовательно, экспоненциальное увеличение числа строк

Ожидаемый результат

Number
--------------------    
1   -- from anchor
2   
1   -- first recursion
2   
1   -- second recursion
2   
1   
2   

Фактическая выработка:

Number
--------------------    
1   -- from anchor
2   
2   -- first recursion  
2   -- second recursion

Ответы [ 2 ]

0 голосов
/ 09 ноября 2018

Из некоторого тестирования выясняется, что рекурсия SQL происходит на глубину, начиная с последней строки, возвращенной из исходного запроса. Как только он достиг MAXRECURSION, запрос завершается с ошибкой (например, The statement terminated. The maximum recursion 2 has been exhausted before statement completion.) и возвращает все результаты, которые он достиг до ошибки. В этом можно убедиться, вставив 2,1 вместо 1,2 в оператор вставки или вставив 1,2,3 в таблицу @Number. Если вы вставите 1,2,3 в @Number, ваши возвращенные результаты будут

1  -- From anchor
2  -- From anchor
3  -- From anchor
3  -- 1st recursion from last row of anchor query
3  -- Recursion from the row from the line above this (2nd recursion)
-- Query terminates after MAXRECURSION reached on an item and does not attempt recursion on 1 or 2

Как правило, при использовании рекурсии вы присоединяете CTE к себе или к какой-либо другой таблице с некоторыми критериями в операторе where или критериями on в объединении, которые ограничивают рекурсию на основе доступных данных (а не просто повторную рекурсию через такие же элементы). Например (используя ту же таблицу @Number из вашего вопроса):

DECLARE @Number TABLE (Number INT);

INSERT @Number
(
    Number
)
VALUES (1),
(2);
WITH _cte2
AS
    (SELECT 
        Number
        ,0 as 'RecursionCount'
    FROM
        @Number

    UNION ALL
        SELECT
        Number
        ,RecursionCount + 1
    FROM
        _cte2
    WHERE
        RecursionCount <= 1
    )
SELECT * FROM _cte2 OPTION (MAXRECURSION 2);

В приведенном выше примере запрос ограничит собственную рекурсию в операторе where и никогда не достигнет MAXRECURSION, поэтому запрос сможет завершиться. Вы также заметите, что порядок возврата результатов подтверждает мое подозрение, что рекурсия - это глубина, начинающаяся с последнего элемента:

Number  RecursionCount
1       0
2       0
2       1
2       2
1       1
1       2
0 голосов
/ 09 ноября 2018

Я немного изменил ваш код на это:

with 

    _cte as (

        SELECT num, 0 iteration
        FROM @Number

        UNION ALL
        SELECT num, iteration + 1
        FROM _cte
        where iteration <= 3 -- comment this line out and compare

    )

    SELECT *
    FROM _cte
    --OPTION (MAXRECURSION 2); -- comment this line out and compare as well

Затем я сравнил результаты с комментариями, когда комментировал «итерацию <<3». Мне потребовалось некоторое время, чтобы понять, но упорядочение результатов оказалось не таким, как я ожидал. </p>

enter image description here

Я думаю, что когда появляется ошибка для циклов max или когда вы включаете опцию отсечения максимальной рекурсии, это как-то обрезает последние результаты, которые вы найдете в строках 7-10.

...