Venant à SQL à partir d'autres langages de programmation, la structure d'une requête récursive semble plutôt étrange. Parcourez-le étape par étape, et il semble s'effondrer.
Prenons l'exemple simple suivant:
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;
Passons en revue.
Tout d'abord, le membre d'ancrage s'exécute et l'ensemble de résultats est placé dans R. Donc R est initialisé à {3, 5, 7}.
Ensuite, l'exécution tombe en dessous de UNION ALL et le membre récursif est exécuté pour la première fois. Il s'exécute sur R (c'est-à-dire sur le R que nous avons actuellement en main: {3, 5, 7}). Il en résulte {9, 25, 49}.
Que fait-il avec ce nouveau résultat? Ajoute-t-il {9, 25, 49} à l'existant {3, 5, 7}, étiquete l'union résultante R, puis poursuit la récursivité à partir de là? Ou redéfinit-il R pour n'être que ce nouveau résultat {9, 25, 49} et fait-il tout l'union plus tard?
Aucun de ces choix n'a de sens.
Si R est maintenant {3, 5, 7, 9, 25, 49} et que nous exécutons l'itération suivante de la récursivité, nous nous retrouverons avec {9, 25, 49, 81, 625, 2401} et nous avons perdu {3, 5, 7}.
Si R n'est plus que {9, 25, 49}, alors nous avons un problème d'étiquetage erroné. R est compris comme l'union de l'ensemble de résultats de membre d'ancrage et de tous les ensembles de résultats de membre récursif suivants. Alors que {9, 25, 49} n'est qu'une composante de R. Ce n'est pas le R complet que nous avons accumulé jusqu'à présent. Par conséquent, écrire le membre récursif comme sélectionnant dans R n'a aucun sens.
J'apprécie certainement ce que @Max Vernon et @Michael S. ont détaillé ci-dessous. À savoir que (1) tous les composants sont créés jusqu'à la limite de récursivité ou l'ensemble nul, puis (2) tous les composants sont réunis. C'est ainsi que je comprends que la récursion SQL fonctionne réellement.
Si nous repensions SQL, nous appliquerions peut-être une syntaxe plus claire et explicite, quelque chose comme ceci:
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 peu comme une preuve inductive en mathématiques.
Le problème avec la récursion SQL telle qu'elle se présente actuellement est qu'elle est écrite de manière confuse. La façon dont il est écrit indique que chaque composant est formé en sélectionnant parmi R, mais cela ne signifie pas le R complet qui a été (ou semble avoir été) construit jusqu'à présent. Cela signifie simplement le composant précédent.