Opis BOL rekurencyjnych CTE opisuje semantykę wykonywania rekurencyjnego jako:
- Podziel wyrażenie CTE na elementy zakotwiczone i rekurencyjne.
- Uruchom elementy zakotwiczone, tworząc pierwsze wywołanie lub podstawowy zestaw wyników (T0).
- Uruchom rekurencyjne elementy z Ti jako wejściem i Ti + 1 jako wyjście.
- Powtarzaj krok 3, dopóki nie zostanie zwrócony pusty zestaw.
- Zwraca zestaw wyników. Jest to UNIA WSZYSTKO od T0 do Tn.
Uwaga: powyższy jest logicznym opisem. Fizyczna kolejność operacji może być nieco inna, jak pokazano tutaj
Stosując to do CTE, oczekiwałbym nieskończonej pętli o następującym wzorze
+-----------+---------+---+---+---+
| Invocation| Results |
+-----------+---------+---+---+---+
| 1 | 1 | 2 | 3 | |
| 2 | 4 | 5 | | |
| 3 | 1 | 2 | 3 | |
| 4 | 4 | 5 | | |
| 5 | 1 | 2 | 3 | |
+-----------+---------+---+---+---+
Dlatego
select a
from cte
where a in (1,2,3)
to wyrażenie Anchor. To wyraźnie zwraca 1,2,3
jakoT0
Następnie uruchomione zostanie wyrażenie rekurencyjne
select a
from cte
except
select a
from r
Z 1,2,3
jako dane wejściowe, które dadzą wynik, 4,5
a T1
następnie podłączenie z powrotem do następnej rundy rekurencji powróci 1,2,3
i tak dalej w nieskończoność.
Jednak tak się nie dzieje. Są to wyniki pierwszych 5 inwokacji
+-----------+---------+---+---+---+
| 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 |
+-----------+---------+---+---+---+
Z użycia OPTION (MAXRECURSION 1)
i regulacji w górę w przyrostach 1
można zauważyć, że wchodzi on w cykl, w którym każdy kolejny poziom będzie ciągle przełączał się między wyjściem 1,2,3,4
a 1,2,3,5
.
Jak omówiono przez @Quassnoi w tym poście na blogu . Wzór obserwowanych wyników wygląda tak, jakby każde wywołanie odbywało się (1),(2),(3),(4),(5) EXCEPT (X)
tam, gdzie X
jest ostatni wiersz z poprzedniego wywołania.
Edycja: Po przeczytaniu doskonałej odpowiedzi SQL Kiwi jasne jest, dlaczego tak się dzieje, i że nie jest to cała historia, ponieważ na stosie wciąż jest mnóstwo rzeczy, których nigdy nie można przetworzyć.
Anchor Emituje 1,2,3
do zawartości stosu klienta3,2,1
3 odpadły stos, zawartość stosu 2,1
LASJ zwraca 1,2,4,5
zawartość stosu5,4,2,1,2,1
5 wysuniętych stosów, zawartość stosu 4,2,1,2,1
LASJ zwraca 1,2,3,4
zawartość stosu4,3,2,1,5,4,2,1,2,1
4 odpadły stos, zawartość stosu 3,2,1,5,4,2,1,2,1
LASJ zwraca 1,2,3,5
zawartość stosu5,3,2,1,3,2,1,5,4,2,1,2,1
5 wysuniętych stosów, zawartość stosu 3,2,1,3,2,1,5,4,2,1,2,1
LASJ zwraca 1,2,3,4
zawartość stosu
4,3,2,1,3,2,1,3,2,1,5,4,2,1,2,1
Jeśli spróbujesz zastąpić element rekurencyjny logicznie równoważnym wyrażeniem (przy braku duplikatów / wartości NULL)
select a
from (
select a
from cte
where a not in
(select a
from r)
) x
Jest to niedozwolone i powoduje błąd „Odwołania rekurencyjne nie są dozwolone w podkwerendach”. więc być może jest to niedopatrzenie, które EXCEPT
w tym przypadku jest dozwolone.
Dodanie:
Firma Microsoft odpowiedziała teraz na moją opinię związaną z Connect jak poniżej
Zgadnięcie Jacka jest poprawne: powinien to być błąd składniowy; rekurencyjne odniesienia nie powinny być dozwolone w EXCEPT
klauzulach. Ten błąd chcemy rozwiązać w nadchodzącym wydaniu usługi. Tymczasem proponuję unikać rekurencyjnych odniesień w EXCEPT
klauzulach.
Ograniczając rekurencję EXCEPT
postępujemy zgodnie ze standardem ANSI SQL, który obejmuje to ograniczenie od czasu wprowadzenia rekurencji (wydaje mi się, że w 1999 r.). Nie ma powszechnej zgody co do semantyki rekurencji EXCEPT
(zwanej także „niestratyfikowaną negacją”) w deklaratywnych językach, takich jak SQL. Ponadto notorycznie trudne (jeśli nie niemożliwe) jest wydajne wdrożenie takiej semantyki (w przypadku baz danych o rozsądnych rozmiarach) w systemie RDBMS.
I wygląda na to, że ostateczna implementacja została wykonana w 2014 roku dla baz danych o poziomie zgodności 120 lub wyższym .
Odwołania rekurencyjne w klauzuli EXCEPT generują błąd zgodny ze standardem ANSI SQL.