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як 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 Ківі зрозуміло і те, чому це відбувається, і що це не вся історія в тому, що на стеку залишається безліч речей, які ніколи не піддаються обробці.
Якір випускає 1,2,3вміст стека клієнта3,2,1
3 вискочив стек, вміст стека 2,1
LASJ повертається 1,2,4,5, Stack Contents5,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навіть у цьому випадку дозволений.
Доповнення:
Корпорація Майкрософт відповіла на мій зворотній зв'язок Connect, як показано нижче
Здогадка Джека правильна: це мала бути синтаксична помилка; в EXCEPTпунктах дійсно не слід допускати рекурсивних посилань . Ми плануємо вирішити цю помилку у майбутньому випуску сервісу. Тим часом я пропоную уникати рекурсивних посилань у EXCEPT
пунктах.
За обмеження рекурсії EXCEPTми дотримуємось стандарту ANSI SQL, який включив це обмеження з моменту введення рекурсії (я вважаю, в 1999 році). Не існує поширеної згоди щодо того, якою має бути семантика для рекурсії EXCEPT(також її називають "нестратифікованим запереченням") в декларативних мовах, таких як SQL. Крім того, вкрай важко (якщо не неможливо) реалізувати таку семантику ефективно (для баз даних розумного розміру) в системі RDBMS.
І схоже, що остаточна реалізація була здійснена у 2014 році для баз даних із рівнем сумісності 120 або вище .
Рекурсивні посилання в пункті EXCEPT генерують помилку відповідно до стандарту ANSI SQL.