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,3jakoT0
Następnie uruchomione zostanie wyrażenie rekurencyjne
select a
from cte
except
select a
from r
Z 1,2,3jako dane wejściowe, które dadzą wynik, 4,5a T1następnie podłączenie z powrotem do następnej rundy rekurencji powróci 1,2,3i 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 1moż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,4a 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 Xjest 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,3do zawartości stosu klienta3,2,1
3 odpadły stos, zawartość stosu 2,1
LASJ zwraca 1,2,4,5zawartość 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 EXCEPTw 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 EXCEPTklauzulach. Ten błąd chcemy rozwiązać w nadchodzącym wydaniu usługi. Tymczasem proponuję unikać rekurencyjnych odniesień w EXCEPT
klauzulach.
Ograniczając rekurencję EXCEPTpostę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.