W przypadku SQL z innych języków programowania struktura zapytania rekurencyjnego wygląda raczej dziwnie. Przejdź go krok po kroku, a wydaje się, że rozpada się.
Rozważ następujący prosty przykład:
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;
Przejdźmy przez to.
Najpierw wykonuje się element zakotwiczający, a zestaw wyników jest umieszczany w R. Tak więc R jest inicjowany na {3, 5, 7}.
Następnie wykonanie spada poniżej wartości UNION ALL, a element rekurencyjny jest wykonywany po raz pierwszy. Wykonuje się na R (to znaczy na R, który obecnie mamy pod ręką: {3, 5, 7}). Daje to {9, 25, 49}.
Co to robi z tym nowym wynikiem? Czy dołącza {9, 25, 49} do istniejącego {3, 5, 7}, oznacza wynikowy związek R, a następnie kontynuuje rekursję z tego miejsca? Czy może redefiniuje R tak, aby był to tylko nowy wynik {9, 25, 49} i czy całe połączenie będzie później?
Żaden wybór nie ma sensu.
Jeśli R wynosi teraz {3, 5, 7, 9, 25, 49} i wykonamy następną iterację rekurencji, to skończymy z {9, 25, 49, 81, 625, 2401} i otrzymamy przegrał {3, 5, 7}.
Jeśli R ma teraz tylko {9, 25, 49}, mamy problem z błędnym etykietowaniem. R jest rozumiane jako połączenie zestawu wyników elementów zakotwiczonych i wszystkich kolejnych zestawów wyników elementów rekurencyjnych. Podczas gdy {9, 25, 49} jest tylko składnikiem R. To nie jest pełne R, które dotychczas nagromadziliśmy. Dlatego napisanie elementu rekurencyjnego jako wybranie z R nie ma sensu.
Z pewnością doceniam to, co @Max Vernon i @Michael S. szczegółowo opisali poniżej. Mianowicie, że (1) wszystkie komponenty są tworzone do limitu rekurencji lub zbioru zerowego, a następnie (2) wszystkie komponenty są łączone razem. W ten sposób rozumiem rekurencję SQL, aby faktycznie działać.
Gdybyśmy przeprojektowywali SQL, być może wymuszalibyśmy bardziej przejrzystą i wyraźną składnię, coś w tym rodzaju:
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;
Coś jak indukcyjny dowód w matematyce.
Problem z rekurencją SQL w obecnej postaci polega na tym, że jest napisany w sposób mylący. Sposób, w jaki jest napisany, mówi, że każdy komponent jest tworzony przez wybranie z R, ale nie oznacza to pełnego R, który został (lub wydaje się, że został zbudowany) do tej pory. Oznacza tylko poprzedni komponent.