recursionCTE如何逐行运行?
我认为recursionCTE的格式已经足够写下来了,但是仍然发现自己不能手动处理一个(假装自己是SQL引擎,用笔和纸来达到结果集) 。 我find了这个 ,这与我正在寻找的接近,但不够详细。 我没有任何问题通过C ++recursion函数追踪并理解它如何运行 – 但对于SQL我不明白为什么或如何引擎知道停止。 锚和recursion块是否每次都被调用,或者在稍后的迭代中跳过锚点? (我对此表示怀疑,但是我试图expression自己对似乎跳跃的方式的困惑)。如果每次调用锚点,那么锚点在最终结果中不会多次出现? 我希望有人可以做第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
想象一个recursion的CTE
就像一个无尽的UNION ALL
:
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
产生任何结果,这意味着停止条件。
理论上,recursionCTE
可以是无限的,但实际上, SQL Server
试图禁止导致无限logging集的查询。
你可能想阅读这篇文章:
- SQL Server:是recursion的CTE的真正集?
CTE使用的algorithm是:
- 执行锚点部分,得到结果r0
- 执行recursion部分,使用r0作为input,并得到结果r1 (非空)
- 执行recursion部分,使用r1作为input,并得到结果r2 (非空)
- 执行recursion部分,使用r3作为input,并获得结果r3 (非空)…
- 执行recursion部分,使用r(n-1)作为input,并输出rn (null)。 这次rn为空,所以我们使用UNION ALL来组合r0,r1,r2 … r(n-1) ,这就是最终的结果
让我们举个例子:
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。
-
执行recursion语句,使用T0作为表来执行查询。 这在查询CTE时会自动发生。
-
如果recursion成员返回一些结果,它将创build一个新的集合T1。 然后再次执行recursion成员,使用T1作为input,如果有任何结果,则创buildT2。
-
第3步继续,直到没有更多的结果产生,或者MAX_RECURSION选项设置的最大recursion次数已经满足。
这个页面可能是最好的解释。 它有一个循序渐进的CTE的执行path。
你可能想要这个链接 。 不,锚点不会被执行多次(不可能,那么union all
要求所有的结果出现)。 在上一个链接的细节。
步骤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步的最后一列是Level。 在每个级别中,相对于已经可用的行添加行。 希望这可以帮助。