La descrizione BOL dei CTE ricorsivi descrive la semantica dell'esecuzione ricorsiva come la seguente:
- Dividi l'espressione CTE in membri anchor e ricorsivi.
- Eseguire i membri di ancoraggio creando la prima chiamata o il set di risultati di base (T0).
- Eseguire i membri ricorsivi con Ti come input e Ti + 1 come output.
- Ripetere il passaggio 3 fino a quando non viene restituito un set vuoto.
- Restituisce il set di risultati. Questa è UNIONE TUTTA da T0 a Tn.
Nota quanto sopra è una descrizione logica . L'ordine fisico delle operazioni può essere leggermente diverso, come illustrato qui
Applicando questo al tuo CTE mi aspetterei un ciclo infinito con il seguente schema
+-----------+---------+---+---+---+
| Invocation| Results |
+-----------+---------+---+---+---+
| 1 | 1 | 2 | 3 | |
| 2 | 4 | 5 | | |
| 3 | 1 | 2 | 3 | |
| 4 | 4 | 5 | | |
| 5 | 1 | 2 | 3 | |
+-----------+---------+---+---+---+
Perché
select a
from cte
where a in (1,2,3)
è l'espressione Anchor. Ciò ritorna chiaramente 1,2,3
comeT0
Successivamente viene eseguita l'espressione ricorsiva
select a
from cte
except
select a
from r
Con 1,2,3
come input che produrrà un output di 4,5
come T1
quindi ricollegandolo che tornerà per il prossimo round di ricorsione tornerà 1,2,3
e così via indefinitamente.
Questo non è ciò che effettivamente accade comunque. Questi sono i risultati delle prime 5 invocazioni
+-----------+---------+---+---+---+
| 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 |
+-----------+---------+---+---+---+
Dall'uso OPTION (MAXRECURSION 1)
e dalla regolazione verso l'alto con incrementi di 1
esso si può vedere che entra in un ciclo in cui ogni livello successivo commuta continuamente tra l'output 1,2,3,4
e 1,2,3,5
.
Come discusso da @Quassnoi in questo post sul blog . Lo schema dei risultati osservati è come se ogni invocazione stesse facendo (1),(2),(3),(4),(5) EXCEPT (X)
dov'è X
l'ultima riga dell'invocazione precedente.
Modifica: dopo aver letto l'eccellente risposta di SQL Kiwi, è chiaro sia il motivo per cui ciò si verifica sia che questa non è l'intera storia in quanto vi sono ancora un sacco di cose rimaste nello stack che non possono mai essere elaborate.
Ancoraggio Emette 1,2,3
al contenuto dello stack client3,2,1
3 fuoriuscite dallo stack, Contenuti dello stack 2,1
Il LASJ ritorna 1,2,4,5
, Stack Stack5,4,2,1,2,1
5 saltar fuori pila, Stack Stack 4,2,1,2,1
Il LASJ restituisce i 1,2,3,4
contenuti dello stack4,3,2,1,5,4,2,1,2,1
4 spuntato fuori pila, pila contenuti 3,2,1,5,4,2,1,2,1
Il LASJ restituisce i 1,2,3,5
contenuti dello stack5,3,2,1,3,2,1,5,4,2,1,2,1
5 saltar fuori pila, Stack Stack 3,2,1,3,2,1,5,4,2,1,2,1
Il LASJ restituisce i 1,2,3,4
contenuti dello stack
4,3,2,1,3,2,1,3,2,1,5,4,2,1,2,1
Se si tenta di sostituire il membro ricorsivo con l'espressione logicamente equivalente (in assenza di duplicati / NULL)
select a
from (
select a
from cte
where a not in
(select a
from r)
) x
Ciò non è consentito e genera l'errore "I riferimenti ricorsivi non sono consentiti nelle sottoquery". quindi forse è una svista EXCEPT
persino consentita in questo caso.
Aggiunta:
Microsoft ha ora risposto al mio Feedback Connect come di seguito
L'ipotesi di Jack è corretta: questo avrebbe dovuto essere un errore di sintassi; i riferimenti ricorsivi non dovrebbero in effetti essere ammessi nelle EXCEPT
clausole. Abbiamo in programma di risolvere questo bug in una prossima versione del servizio. Nel frattempo, suggerirei di evitare riferimenti ricorsivi nelle EXCEPT
clausole.
Nel limitare la ricorsione EXCEPT
seguiamo lo standard ANSI SQL, che ha incluso questa restrizione sin dall'introduzione della ricorsione (nel 1999 credo). Non esiste un accordo diffuso su ciò che dovrebbe essere la semantica per la ricorsione EXCEPT
(anche chiamata "negazione non stratificata") in linguaggi dichiarativi come SQL. Inoltre, è notoriamente difficile (se non impossibile) implementare tale semantica in modo efficiente (per database di dimensioni ragionevoli) in un sistema RDBMS.
E sembra che l'eventuale implementazione sia stata effettuata nel 2014 per database con un livello di compatibilità di 120 o superiore .
I riferimenti ricorsivi in una clausola EXCEPT generano un errore conforme allo standard ANSI SQL.