再帰的CTEの形式は十分に理解できていると思いますが、それでも手動で処理できないことに不満を感じています(自分でSQLエンジンのふりをして、ペンと紙で結果セットに到達します) 。 私はこれを見つけました 、これは私が探しているものに近いですが、十分に詳細ではありません。 C++再帰関数をトレースして、その実行方法を理解することに問題はありませんが、SQLの場合、エンジンが停止する理由や方法を理解していません。アンカーと再帰ブロックは毎回呼び出されますか、それとも後の反復でアンカーがスキップされますか? (疑わしいですが、ジャンプのように見える方法について混乱を表現しようとしています。)アンカーが毎回呼び出された場合、最終結果にアンカーが複数回表示されないようにするにはどうすればよいですか?誰かが1行目2行目などを分解できることを願っています。結果セットが蓄積されると、何が起こり、何が「メモリ内」にあるのかがわかります。
私は自分の このページの例 を盗む自由を取りました。それが最も理解しやすいように思われるからです。
DECLARE @tbl TABLE (
Id INT
, [Name] VARCHAR(20)
, ParentId INT
)
INSERT INTO @tbl( Id, Name, ParentId )
VALUES
(1, 'Europe', NULL)
,(2, 'Asia', NULL)
,(3, 'Germany', 1)
,(4, 'UK', 1)
,(5, 'China', 2)
,(6, 'India', 2)
,(7, 'Scotland', 4)
,(8, 'Edinburgh', 7)
,(9, 'Leith', 8)
;
WITH abcd
AS (
-- anchor
SELECT id, Name, ParentID,
CAST(Name AS VARCHAR(1000)) AS Path
FROM @tbl
WHERE ParentId IS NULL
UNION ALL
--recursive member
SELECT t.id, t.Name, t.ParentID,
CAST((a.path + '/' + t.Name) AS VARCHAR(1000)) AS "Path"
FROM @tbl AS t
JOIN abcd AS a
ON t.ParentId = a.id
)
SELECT * FROM abcd
無限のUNION ALL
のように再帰的なCTE
を考えてみてください。
WITH rows AS
(
SELECT *
FROM mytable
WHERE anchor_condition
),
rows2 AS
(
SELECT *
FROM set_operation(mytable, rows)
),
rows3 AS
(
SELECT *
FROM set_operation(mytable, rows2)
),
…
SELECT *
FROM rows
UNION ALL
SELECT *
FROM rows2
UNION ALL
SELECT *
FROM rows3
UNION ALL
…
あなたの場合、それは次のようになります。
WITH abcd1 AS
(
SELECT *
FROM @tbl t
WHERE ParentId IS NULL
),
abcd2 AS
(
SELECT t.*
FROM abcd1
JOIN @tbl t
ON t.ParentID = abcd1.id
),
abcd3 AS
(
SELECT t.*
FROM abcd2
JOIN @tbl t
ON t.ParentID = abcd2.id
),
abcd4 AS
(
SELECT t.*
FROM abcd3
JOIN @tbl t
ON t.ParentID = abcd3.id
),
abcd5 AS
(
SELECT t.*
FROM abcd4
JOIN @tbl t
ON t.ParentID = abcd4.id
),
abcd6 AS
(
SELECT t.*
FROM abcd5
JOIN @tbl t
ON t.ParentID = abcd5.id
)
SELECT *
FROM abcd1
UNION ALL
SELECT *
FROM abcd2
UNION ALL
SELECT *
FROM abcd3
UNION ALL
SELECT *
FROM abcd4
UNION ALL
SELECT *
FROM abcd5
UNION ALL
SELECT *
FROM abcd6
abcd6
は結果を生成しないため、これは停止状態を意味します。
理論的には、再帰的なCTE
は無限大になる可能性がありますが、実際には、SQL Server
は無限のレコードセットにつながるクエリを禁止しようとします。
あなたはこの記事を読みたいと思うかもしれません:
CTEが使用するアルゴリズムは次のとおりです。
例を見てみましょう:
WITH cte ( value )
AS (
SELECT 1
UNION ALL
SELECT value + 1
FROM cte
WHERE value < 4
)
SELECT *
FROM cte
このクエリの結果は次のとおりです。
value
-----------
1
2
3
4
(4 row(s) affected)
それを段階的に調べてみましょう:
Execute anchor query (SELECT 1), we got:
r0 = 1
cte = r0 = 1
|
|
V
Now we execute
SELECT value + 1 FROM cte WHERE value < 4
Since cte is r0 (only has 1), we got:
r1 = 2
cte = r1 = 2
|
|
V
Now we execute
SELECT value + 1 FROM cte WHERE value < 4
Since cte is r1 (only has 2), we got:
r2 = 3
cte = r2 = 3
|
|
V
Now we execute
SELECT value + 1 FROM cte WHERE value < 4
Since cte is r2 (only has 3), we got:
r3 = 4
cte = r3 = 4
|
|
V
Now we execute
SELECT value + 1 FROM cte WHERE value < 4
Since cte is r3 (only has 4), we got:
r4 = NULL (because r3 (4) is equal to 4, not less than 4)
Now we stop the recursion!
|
|
V
Let's calculate the final result:
R = r0 union all
r1 union all
r2 union all
r3 union all
= 1 union all
2 union all
3 union all
4 union all
= 1
2
3
4
私はそれがこのように崩壊すると思います:
アンカーステートメントが実行されます。これにより、基本セットまたはT0と呼ばれる一連の結果が得られます。
クエリを実行するテーブルとしてT0を使用して、再帰ステートメントが実行されます。これは、CTEを照会すると自動的に発生します。
再帰メンバーが何らかの結果を返す場合、新しいセットT1を作成します。次に、再帰メンバーが実行されますagain、入力としてT1を使用し、結果がある場合はT2を作成します。
ステップ3は、結果が生成されなくなるまで続きます。OR MAX_RECURSIONオプションで設定された、再帰の最大数に達しました。
このページ おそらくそれを最もよく説明しています。 CTEの実行パスのステップバイステップのウォークスルーがあります。
ステップ1:
1 Europe NULL Europe
2 Asia NULL Asia
ステップ2:
1 Europe NULL Europe
2 Asia NULL Asia
3 Germany 1 Europe/Germany
4 UK 1 Europe/UK
5 China 2 Asia/China
6 India 2 Asia/India
ステップ3:
1 Europe NULL Europe
2 Asia NULL Asia
3 Germany 1 Europe/Germany
4 UK 1 Europe/UK
5 China 2 Asia/China
6 India 2 Asia/India
7 Scotland 4 Europe/UK/Scotland
ステップ4:
1 Europe NULL Europe
2 Asia NULL Asia
3 Germany 1 Europe/Germany
4 UK 1 Europe/UK
5 China 2 Asia/China
6 India 2 Asia/India
7 Scotland 4 Europe/UK/Scotland
8 Edinburgh 7 Europe/UK/Scotland/Edinburgh
ステップ5:
1 Europe NULL Europe 0
2 Asia NULL Asia 0
3 Germany 1 Europe/Germany 1
4 UK 1 Europe/UK 1
5 China 2 Asia/China 1
6 India 2 Asia/India 1
7 Scotland 4 Europe/UK/Scotland 2
8 Edinburgh 7 Europe/UK/Scotland/Edinburgh 3
9 Leith 8 Europe/UK/Scotland/Edinburgh/Leith 4
ステップ5の最後の列はレベルです。各レベルで、すでに使用可能なものに関して行が追加されます。お役に立てれば。
あなたはおそらく欲しかった このリンク 。いいえ、アンカーは複数回実行されません(実行できなかった場合は、union all
すべての結果が表示される必要があります)。詳細は前のリンクにあります。