我一直在阅读CTE Recursion的内部工作原理,我仍然不确定SQL Server是如何在源代码中实现Anchor元素的,以及SQL Server查询执行引擎/查询优化器是如何解释这些指令集的。
为了遵循逻辑,我仔细阅读了以下文章:http://sqlchitchat.com/sqldev/tsql/recursive-cte/
让我们以上述文章中的示例1为例:https://github.com/dmincic/SQLCHITCHAT/blob/master/CTE/Sql/Example1_cteQuery.sql
DECLARE @EmpId INT = 3;
;WITH cte1 AS
(
-- Anchor Element
SELECT EmpId
,MgrId
FROM dbo.Employees
WHERE MgrId = @EmpId
UNION ALL
-- Recursive Element
SELECT e.EmpId
,e.MgrId
FROM dbo.Employees e
INNER JOIN cte1 c
ON e.MgrId = c.EmpId
)
-- Invoke CTE Recursion
SELECT cte1.EmpId
,cte1.MgrId
FROM cte1;如果您向下滚动到本文的中间,它将讨论锚元素在第二次迭代/第一次递归中的作用:
第二个迭代begins.This是第一个递归。锚部分在第一次迭代中发挥了作用,从现在起只返回空集。但是,递归部分现在可以在内部联接操作符中引用它以前的结果(第一次迭代之后的cte1值)。表操作产生第二次迭代的结果,如下图所示。
图5:https://sqlchitchat.com/wp-content/uploads/2019/10/2nd_Iteration1.png

在图中,您可以看到为EmpId和MgrId绘制的空集(嗯,元组)。
然后我阅读了SQL递归实际上是如何工作的?,从所提供的答案来看,Anchor元素甚至没有为N+1迭代进行评估。
那么,是哪一个呢?QE/QO是在每次迭代中评估Anchor,还是在递归期间返回空元组,还是在递归过程中跳过Anchor,从而永远不会返回空元组?
为了澄清我的问题,下面是我要问的问题:底层代码是否类似于:
static void Main(string[] args)
{
int Number = 0;
long Result;
// Anchor part
Number = GetNumberFromProgramArguments();
// Anchor part is only executed once
// Recursive part
Result = CalculateFactorial(Number);
}
public static long CalculateFactorial(int number)
{
// Termination check
if (number == 0)
return 1;
// Recursive call
return number * CalculateFactorial(number - 1);
}或者更像这样:
static void Main(string[] args)
{
int Number = 0;
long Result;
int RecursionLevel = 0;
// Recursive part
Result = CalculateFactorial(Number, RecursionLevel);
}
public static long CalculateFactorial(int number, int recursionLevel)
{
// Run Anchor part only if Recursion Level is 0
if (recursionLevel == 0)
// Anchor part
number = GetNumberFromProgramArguments();
// Termination check
if (number == 0)
return 1;
// Increment recursion
recursionLevel++;
// Recursive call
return number * CalculateFactorial(number - 1, recursionLevel);
}这不是最好的例子,因为我一直在关注表格,而不是数字,但我希望你能理解我的观点。为什么文章指出在递归期间,Anchor作为一个空元组被评估和返回?这就是我在这里感到困惑的地方。这没有意义。SQL执行计划没有显示为N+1迭代计算的Anchor部分,只是第一次迭代。如果是这样的话,那么锚部分在递归期间将永远不会返回任何内容,因为执行永远不会到达代码的那个部分,甚至可以计算锚点。我不明白为什么图5显示在递归期间锚点被计算为空元组。它甚至不应该显示任何东西。
是的,我了解到,如果可以的话,递归正在抓住上一次迭代。但是,从图5和我引用的段落中,博客指出锚点执行每一次迭代,并返回递归部分的空元组,这在SQL执行计划中并不明显。“执行计划”显示“锚”一次又一次执行。你知道我为什么在这里搞不懂吗?
发布于 2023-02-14 20:28:42
您所指的锚部分将只进行一次评估。您可以将其与归纳证明中的基本情况进行比较。这可以用一个简单的例子来说明:
with t (n,origin) as (
select 1, 'Anchor '
union all
select n+1, 'Recursive'
from t where n<10
)
select * from t;
n origin
1 Anchor
2 Recursive
3 Recursive
4 Recursive
5 Recursive
6 Recursive
7 Recursive
8 Recursive
9 Recursive
10 Recursive如果多次评估的锚部分将是结果的一部分,因为没有可以计算为false的谓词。
https://dba.stackexchange.com/questions/323511
复制相似问题