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是:

  1. 执行锚点部分,得到结果r0
  2. 执行recursion部分,使用r0作为input,并得到结果r1 (非空)
  3. 执行recursion部分,使用r1作为input,并得到结果r2 (非空)
  4. 执行recursion部分,使用r3作为input,并获得结果r3 (非空)…
  5. 执行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 

我认为它是这样的:

  1. 定位语句被执行。 这给你一组结果,称为基本集合,或T0。

  2. 执行recursion语句,使用T0作为表来执行查询。 这在查询CTE时会自动发生。

  3. 如果recursion成员返回一些结果,它将创build一个新的集合T1。 然后再次执行recursion成员,使用T1作为input,如果有任何结果,则创buildT2。

  4. 第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。 在每个级别中,相对于已经可用的行添加行。 希望这可以帮助。