BOL-описание рекурсивных CTE описывает семантику рекурсивного выполнения следующим образом:
- Разделите выражение CTE на якорные и рекурсивные члены.
- Запустите элемент привязки, создавая первый вызов или базовый набор результатов (T0).
- Запустите рекурсивный элемент (ы) с Ti в качестве входа и Ti + 1 в качестве выхода.
- Повторяйте шаг 3, пока не будет возвращен пустой набор.
- Вернуть набор результатов. Это СОЮЗ ВСЕХ от T0 до Tn.
Обратите внимание, что выше приведено логическое описание. Физический порядок операций может несколько отличаться, как показано здесь
Применяя это к вашему CTE, я бы ожидал бесконечный цикл со следующей схемой
+-----------+---------+---+---+---+
| Invocation| Results |
+-----------+---------+---+---+---+
| 1 | 1 | 2 | 3 | |
| 2 | 4 | 5 | | |
| 3 | 1 | 2 | 3 | |
| 4 | 4 | 5 | | |
| 5 | 1 | 2 | 3 | |
+-----------+---------+---+---+---+
Потому что
select a
from cte
where a in (1,2,3)
Якорное выражение. Это явно возвращается 1,2,3
какT0
После этого выполняется рекурсивное выражение
select a
from cte
except
select a
from r
Если в 1,2,3
качестве входных данных будет получен вывод 4,5
as, T1
то подключение к нему для следующего раунда рекурсии будет возвращаться 1,2,3
и так до бесконечности.
Это не то, что на самом деле происходит, однако. Это результаты первых 5 вызовов
+-----------+---------+---+---+---+
| Invocation| Results |
+-----------+---------+---+---+---+
| 1 | 1 | 2 | 3 | |
| 2 | 1 | 2 | 4 | 5 |
| 3 | 1 | 2 | 3 | 4 |
| 4 | 1 | 2 | 3 | 5 |
| 5 | 1 | 2 | 3 | 4 |
+-----------+---------+---+---+---+
Из использования OPTION (MAXRECURSION 1)
и корректировки с приращением вверх 1
видно, что он входит в цикл, где каждый последующий уровень будет непрерывно переключаться между выводом 1,2,3,4
и 1,2,3,5
.
Как обсуждается @Quassnoi в этом блоге . Картина наблюдаемых результатов , как если бы каждый вызов делает , (1),(2),(3),(4),(5) EXCEPT (X)
где X
это последняя строка из предыдущего вызова.
Редактирование: После прочтения превосходного ответа SQL Kiwi становится понятно, почему это происходит, и что это не вся история, поскольку в стеке все еще остается множество вещей, которые никогда не могут быть обработаны.
Якорь отправляет 1,2,3
содержимое стека клиента3,2,1
3 вытолкнутых стека, содержимое стека 2,1
LASJ возвращается 1,2,4,5
, содержимое стека5,4,2,1,2,1
5 оторванных стеков, содержимое стека 4,2,1,2,1
LASJ возвращает 1,2,3,4
содержимое стека4,3,2,1,5,4,2,1,2,1
4 вытолкнутых стека, содержимое стека 3,2,1,5,4,2,1,2,1
LASJ возвращает 1,2,3,5
содержимое стека5,3,2,1,3,2,1,5,4,2,1,2,1
5 оторванных стеков, содержимое стека 3,2,1,3,2,1,5,4,2,1,2,1
LASJ возвращает 1,2,3,4
содержимое стека
4,3,2,1,3,2,1,3,2,1,5,4,2,1,2,1
Если вы попытаетесь заменить рекурсивный член логически эквивалентным (при отсутствии дубликатов / NULL) выражением
select a
from (
select a
from cte
where a not in
(select a
from r)
) x
Это недопустимо и вызывает ошибку «Рекурсивные ссылки не разрешены в подзапросах». так что, возможно, это недосмотр EXCEPT
в данном случае.
Дополнение:
Microsoft ответила на мой отзыв о подключении, как показано ниже
Догадка Джека верна: это должна была быть синтаксическая ошибка; рекурсивные ссылки действительно не должны быть разрешены в EXCEPT
пунктах. Мы планируем устранить эту ошибку в следующем выпуске службы. А пока я бы предложил избегать рекурсивных ссылок в EXCEPT
статьях.
Ограничивая рекурсию, EXCEPT
мы следуем стандарту ANSI SQL, который включал это ограничение с тех пор, как была введена рекурсия (я полагаю, в 1999 году). Не существует широко распространенного соглашения о том, какой должна быть семантика для рекурсии EXCEPT
(также называемой «безусловным отрицанием») в декларативных языках, таких как SQL. Кроме того, общеизвестно, что трудно (если не невозможно) эффективно реализовать такую семантику (для баз данных разумного размера) в системе RDBMS.
И похоже, что окончательная реализация была сделана в 2014 году для баз данных с уровнем совместимости 120 или выше .
Рекурсивные ссылки в предложении EXCEPT генерируют ошибку в соответствии со стандартом ANSI SQL.