他のプログラミング言語からSQLに移行すると、再帰クエリの構造はかなり奇妙に見えます。一歩ずつ歩いていくと、バラバラになっているようです。
次の簡単な例を考えてみましょう。
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;
それを見てみましょう。
最初に、アンカーメンバーが実行され、結果セットがRに格納されます。したがって、Rは{3、5、7}に初期化されます。
次に、実行はUNION ALLを下回り、再帰メンバーが初めて実行されます。Rで実行されます(つまり、現在手元にあるRで実行されます:{3、5、7})。この結果は{9、25、49}になります。
この新しい結果はどうなりますか?既存の{3、5、7}に{9、25、49}を追加し、結果のユニオンRにラベルを付け、そこから再帰を続行しますか?または、Rをこの新しい結果{9、25、49}のみに再定義し、後ですべての結合を行いますか?
どちらの選択も意味がありません。
Rが{3、5、7、9、25、49}であり、再帰の次の反復を実行すると、{9、25、49、81、625、2401}になり、 {3、5、7}を失った。
Rが{9、25、49}のみの場合、ラベル付けの問題があります。Rは、アンカーメンバーの結果セットと後続のすべての再帰メンバーの結果セットの和集合であると理解されます。一方、{9、25、49}はRの構成要素にすぎません。これまでに発生した完全なRではありません。したがって、Rから選択するように再帰メンバーを記述することは意味がありません。
@Max Vernonと@Michael S.が以下に詳述していることは確かに感謝しています。つまり、(1)すべてのコンポーネントが再帰制限またはヌルセットまで作成され、(2)すべてのコンポーネントが結合されます。これが、SQL再帰が実際に機能することを理解する方法です。
SQLを再設計する場合は、次のような、より明確で明示的な構文を適用する可能性があります。
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;
数学の帰納的証明のようなもの。
現在のSQL再帰の問題は、混乱を招くように書かれていることです。書かれた方法では、各コンポーネントはRから選択することによって形成されると言いますが、これまでに構築された(または構築されたように見える)完全なRを意味するものではありません。前のコンポーネントを意味するだけです。