Wenn man von anderen Programmiersprachen zu SQL kommt, sieht die Struktur einer rekursiven Abfrage ziemlich seltsam aus. Gehen Sie Schritt für Schritt durch, und es scheint auseinander zu fallen.
Betrachten Sie das folgende einfache Beispiel:
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;
Lass uns durchgehen.
Zuerst wird das Ankerelement ausgeführt und die Ergebnismenge in R abgelegt. Daher wird R auf {3, 5, 7} initialisiert.
Dann fällt die Ausführung unter UNION ALL und das rekursive Element wird zum ersten Mal ausgeführt. Es wird auf R ausgeführt (d. H. Auf dem R, das wir derzeit in der Hand haben: {3, 5, 7}). Dies führt zu {9, 25, 49}.
Was macht es mit diesem neuen Ergebnis? Wird {9, 25, 49} an die vorhandene {3, 5, 7} angehängt, die resultierende Vereinigung R bezeichnet und die Rekursion von dort fortgesetzt? Oder definiert es R neu, nur dieses neue Ergebnis zu sein {9, 25, 49} und die gesamte Vereinigung später?
Weder Wahl macht Sinn.
Wenn R jetzt {3, 5, 7, 9, 25, 49} ist und wir die nächste Iteration der Rekursion ausführen, erhalten wir {9, 25, 49, 81, 625, 2401} hat {3, 5, 7} verloren.
Wenn R jetzt nur {9, 25, 49} ist, haben wir ein Problem mit der falschen Bezeichnung. Unter R versteht man die Vereinigung der Ankerelement-Ergebnismenge und aller nachfolgenden rekursiven Element-Ergebnismengen. Wobei {9, 25, 49} nur eine Komponente von R ist. Es ist nicht das vollständige R, das wir bisher angehäuft haben. Daher macht es keinen Sinn, das rekursive Element so zu schreiben, dass es aus R auswählt.
Ich weiß auf jeden Fall zu schätzen, was @Max Vernon und @Michael S. unten beschrieben haben. Das heißt, dass (1) alle Komponenten bis zur Rekursionsgrenze oder Nullmenge erstellt werden und dann (2) alle Komponenten zusammen unioniert werden. So verstehe ich, dass die SQL-Rekursion tatsächlich funktioniert.
Wenn wir SQL neu entwerfen würden, würden wir möglicherweise eine klarere und explizitere Syntax durchsetzen, etwa so:
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;
Irgendwie ein induktiver Beweis in der Mathematik.
Das Problem mit der aktuellen SQL-Rekursion ist, dass sie verwirrend geschrieben ist. Die Art und Weise, wie es geschrieben steht, besagt, dass jede Komponente durch Auswahl von R gebildet wird, aber es bedeutet nicht, dass das gesamte R, das bisher konstruiert wurde (oder zu sein scheint). Es bedeutet nur die vorherige Komponente.