Die BOL-Beschreibung von rekursiven CTEs beschreibt die Semantik der rekursiven Ausführung wie folgt:
- Teilen Sie den CTE-Ausdruck in Anker- und rekursive Member auf.
- Führen Sie die Ankerelemente aus, die die erste Aufruf- oder Basisergebnismenge (T0) erstellen.
- Führen Sie die rekursiven Elemente mit Ti als Eingabe und Ti + 1 als Ausgabe aus.
- Wiederholen Sie Schritt 3, bis ein leerer Satz zurückgegeben wird.
- Gibt die Ergebnismenge zurück. Dies ist eine UNION ALL von T0 bis Tn.
Beachten Sie, dass das oben Gesagte eine logische Beschreibung ist. Die physikalische Reihenfolge der Operationen kann, wie hier dargestellt, etwas anders sein
Wenn Sie dies auf Ihren CTE anwenden, würde ich eine Endlosschleife mit dem folgenden Muster erwarten
+-----------+---------+---+---+---+
| Invocation| Results |
+-----------+---------+---+---+---+
| 1 | 1 | 2 | 3 | |
| 2 | 4 | 5 | | |
| 3 | 1 | 2 | 3 | |
| 4 | 4 | 5 | | |
| 5 | 1 | 2 | 3 | |
+-----------+---------+---+---+---+
weil
select a
from cte
where a in (1,2,3)
ist der Ankerausdruck. Dies zeigt deutlich zurück 1,2,3
alsT0
Danach wird der rekursive Ausdruck ausgeführt
select a
from cte
except
select a
from r
Mit 1,2,3
as input, das einen Output von 4,5
as T1
then ergibt, kehrt das Wiedereinstecken für die nächste Runde der Rekursion 1,2,3
auf unbestimmte Zeit zurück und so weiter.
Dies ist jedoch nicht der Fall. Dies sind die Ergebnisse der ersten 5 Aufrufe
+-----------+---------+---+---+---+
| 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 |
+-----------+---------+---+---+---+
Aus der Verwendung OPTION (MAXRECURSION 1)
und Einstellung in Schritten nach oben 1
ist ersichtlich, dass sie in einen Zyklus eintritt, in dem jede aufeinanderfolgende Ebene kontinuierlich zwischen Ausgabe 1,2,3,4
und umschaltet 1,2,3,5
.
Wie von @Quassnoi in diesem Blog-Beitrag besprochen . Das Muster der beobachteten Ergebnisse ist so, als würde jeder Aufruf dort ausgeführt, (1),(2),(3),(4),(5) EXCEPT (X)
wo X
sich die letzte Zeile des vorherigen Aufrufs befindet.
Bearbeiten: Nach dem Lesen von SQL Kiwis exzellenter Antwort ist sowohl klar, warum dies auftritt, als auch, dass dies nicht die ganze Geschichte ist.
Anker wird an den Inhalt 1,2,3
des Client-Stacks gesendet3,2,1
3 herausgesprungener Stapel, Stapelinhalt 2,1
Der LASJ kehrt zurück 1,2,4,5
, Stack Contents5,4,2,1,2,1
5 herausgesprungener Stapel, Stapelinhalt 4,2,1,2,1
Die LASJ gibt 1,2,3,4
Stapel Inhalt4,3,2,1,5,4,2,1,2,1
4 herausgesprungener Stapel, Stapelinhalt 3,2,1,5,4,2,1,2,1
Die LASJ gibt 1,2,3,5
Stapel Inhalt5,3,2,1,3,2,1,5,4,2,1,2,1
5 herausgesprungener Stapel, Stapelinhalt 3,2,1,3,2,1,5,4,2,1,2,1
Die LASJ gibt 1,2,3,4
Stapel Inhalt
4,3,2,1,3,2,1,3,2,1,5,4,2,1,2,1
Wenn Sie versuchen, das rekursive Element durch den logisch äquivalenten Ausdruck (in Abwesenheit von Duplikaten / NULL-Werten) zu ersetzen
select a
from (
select a
from cte
where a not in
(select a
from r)
) x
Dies ist nicht zulässig und löst den Fehler "Rekursive Referenzen sind in Unterabfragen nicht zulässig" aus. Vielleicht ist es ein Versehen, EXCEPT
das in diesem Fall sogar erlaubt ist.
Zusatz:
Microsoft hat jetzt auf mein Connect Feedback wie unten
Jacks Vermutung ist richtig: Dies hätte ein Syntaxfehler sein müssen. rekursive Verweise sollten in der Tat nicht in EXCEPT
Klauseln erlaubt sein . Wir planen, diesen Fehler in einer kommenden Service-Version zu beheben. In der Zwischenzeit würde ich vorschlagen, rekursive Verweise in EXCEPT
Klauseln zu vermeiden .
Bei der Einschränkung der Rekursion EXCEPT
folgen wir dem ANSI-SQL-Standard, der diese Einschränkung seit Einführung der Rekursion (glaube ich 1999) enthält. EXCEPT
In deklarativen Sprachen wie SQL gibt es keine weit verbreitete Übereinstimmung darüber, wie die Semantik für die Rekursion sein soll (auch als "nicht geschichtete Negation" bezeichnet). Darüber hinaus ist es notorisch schwierig (wenn nicht unmöglich), eine solche Semantik effizient (für Datenbanken mit angemessener Größe) in einem RDBMS-System zu implementieren.
Und es sieht so aus, als ob die spätere Implementierung im Jahr 2014 für Datenbanken mit einem Kompatibilitätsgrad von 120 oder höher durchgeführt wurde .
Rekursive Verweise in einer EXCEPT-Klausel erzeugen einen Fehler in Übereinstimmung mit dem ANSI-SQL-Standard.