Venendo a SQL da altri linguaggi di programmazione, la struttura di una query ricorsiva sembra piuttosto strana. Attraversalo passo dopo passo, e sembra cadere a pezzi.
Considera il seguente semplice esempio:
CREATE TABLE #NUMS
(N BIGINT);
INSERT INTO #NUMS
VALUES (3), (5), (7);
WITH R AS
(
SELECT N FROM #NUMS
UNION ALL
SELECT N*N AS N FROM R WHERE N*N < 10000000
)
SELECT N FROM R ORDER BY N;
Camminiamo attraverso di esso.
Innanzitutto, l'elemento di ancoraggio viene eseguito e il set di risultati viene inserito in R. Quindi R viene inizializzato su {3, 5, 7}.
Quindi, l'esecuzione scende al di sotto di UNION ALL e il membro ricorsivo viene eseguito per la prima volta. Viene eseguito su R (ovvero su R che attualmente abbiamo in mano: {3, 5, 7}). Ciò si traduce in {9, 25, 49}.
Cosa fa con questo nuovo risultato? Aggiunge {9, 25, 49} all'esistente {3, 5, 7}, etichetta l'unione risultante R, e quindi continua con la ricorsione da lì? O ridefinisce R come solo questo nuovo risultato {9, 25, 49} e fa tutto il sindacato più tardi?
Nessuna scelta ha senso.
Se R è ora {3, 5, 7, 9, 25, 49} e eseguiamo la prossima iterazione della ricorsione, allora finiremo con {9, 25, 49, 81, 625, 2401} e abbiamo perso {3, 5, 7}.
Se R ora è solo {9, 25, 49}, allora abbiamo un problema di etichettatura errata. R è inteso come l'unione dell'insieme di risultati dell'elemento di ancoraggio e di tutti i successivi insiemi di risultati dell'elemento ricorsivo. Considerando che {9, 25, 49} è solo una componente di R. Non è la R completa che abbiamo accumulato finora. Pertanto, scrivere l'elemento ricorsivo come selezionando da R non ha senso.
Apprezzo sicuramente ciò che @Max Vernon e @Michael S. hanno dettagliato di seguito. Vale a dire che (1) tutti i componenti vengono creati fino al limite di ricorsione o set null, quindi (2) tutti i componenti vengono uniti insieme. Questo è il modo in cui capisco la ricorsione SQL per funzionare davvero.
Se stessimo ridisegnando SQL, forse applicheremmo una sintassi più chiara ed esplicita, qualcosa del genere:
WITH R AS
(
SELECT N
INTO R[0]
FROM #NUMS
UNION ALL
SELECT N*N AS N
INTO R[K+1]
FROM R[K]
WHERE N*N < 10000000
)
SELECT N FROM R ORDER BY N;
Un po 'come una prova induttiva in matematica.
Il problema con la ricorsione di SQL così com'è attualmente è che è scritto in modo confuso. Il modo in cui è scritto dice che ogni componente è formato selezionando da R, ma non significa che l'intera R che è stata (o, sembra essere stata costruita) finora. Significa solo il componente precedente.