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.