Sql server 为什么Transact-SQL中的递归CTE需要UNION ALL而不是UNION?
我明白锚是必要的,这是有道理的。我知道需要一个Sql server 为什么Transact-SQL中的递归CTE需要UNION ALL而不是UNION?,sql-server,tsql,recursion,common-table-expression,Sql Server,Tsql,Recursion,Common Table Expression,我明白锚是必要的,这是有道理的。我知道需要一个联合ALL,如果递归CTE没有,它就是不工作。。。但我找不到一个很好的解释来解释为什么会这样。所有文档都只是说明您需要它 为什么我们不能在递归查询中使用UNION而不是UNION-ALL?在更深层次的递归中不包含重复项似乎是个好主意,不是吗?我认为,类似的东西应该已经在幕后发挥作用了。这纯粹是猜测,但我要说的是,联合会确保每个迭代的结果都可以单独计算。从本质上说,它确保了一个迭代不会干扰另一个迭代 联合需要在后台执行排序操作,这可能会修改以前迭代的结
联合ALL
,如果递归CTE没有,它就是不工作。。。但我找不到一个很好的解释来解释为什么会这样。所有文档都只是说明您需要它
为什么我们不能在递归查询中使用
UNION
而不是UNION-ALL
?在更深层次的递归中不包含重复项似乎是个好主意,不是吗?我认为,类似的东西应该已经在幕后发挥作用了。这纯粹是猜测,但我要说的是,联合会确保每个迭代的结果都可以单独计算。从本质上说,它确保了一个迭代不会干扰另一个迭代
联合需要在后台执行排序操作,这可能会修改以前迭代的结果。程序不应更改调用堆栈中先前调用的状态,它应使用输入参数和后续迭代的结果(在过程设置中)与之交互。这可能适用于基于集合的操作,因此也适用于SQLServer的递归CTE
我可能错了,深夜的大脑转储不是100%可靠的:)
编辑(只是另一个想法):
当递归开始时,您有一个调用堆栈。此堆栈中的每个级别都开始计算其结果,但应该等待所有后续调用的结果,然后才能完成并返回其结果。UNION将尝试消除重复,但在达到终止条件之前,您没有任何记录(最终结果将自下而上生成),但后续调用的结果是上面的调用所必需的。联盟最终将被简化为一个独立的联盟。我想原因是他们认为这是一个不值得实现的优先功能。它看起来像
UNION
和UNION-ALL
如果您对此功能有充分的理由,您可以通过(或其替代者的URL)提供反馈
防止添加重复可能很有用,因为在后一步中添加到前一步的重复行几乎总是导致无限循环或超过最大递归限制
世界上有相当多的地方使用代码来演示UNION
,如下所示
这篇文章解释了如何。他们不会在“幕后”做那样的事。堆栈假脱机会在运行时删除行,因此无法知道后面的行是否与已删除的行重复。支持UNION
需要一种稍微不同的方法
同时,您可以很容易地在多语句TVF中实现相同的功能
下面举一个愚蠢的例子()
将UNION
更改为UNION ALL
并在末尾添加一个DISTINCT
,不会将您从无限递归中解救出来
但您可以将其实现为
CREATE FUNCTION dbo.F ()
RETURNS @R TABLE(n INT PRIMARY KEY WITH (IGNORE_DUP_KEY = ON))
AS
BEGIN
INSERT INTO @R
VALUES (0); --anchor
WHILE @@ROWCOUNT > 0
BEGIN
INSERT INTO @R
SELECT ( N + 1 )%10
FROM @R
END
RETURN
END
GO
SELECT *
FROM dbo.F ()
上面使用忽略重复键
丢弃重复项。如果列列表太宽,无法编制索引,则需要使用DISTINCT
和NOT EXISTS
。您可能还需要一个参数来设置最大递归次数并避免无限循环。这里是对后期推测的一个很好的解释:
优化说明:。。。。。。运行上述示例只需要很少的内存。但是,如果示例使用UNION而不是UNIONALL,那么SQLite必须保留所有以前生成的内容,以便检查重复的内容。因此,在可行的情况下,程序员应该努力使用UNION ALL而不是UNION
我们能看一些代码吗,因为我不明白你的意思。要不要评论一下?@Sami递归CTE的定义需要一个锚查询
UNION ALL
'd到一个递归查询。我在问为什么它需要是UNION ALL而不是UNION,我想优化器可能已经在以某种形式进行了,因为这就是语法。如果您的代码有问题,重复的项目,请发布it@Ezin82我的代码没有问题,而且我知道语法。我知道如何在SQL中递归。我想问的是,当递归查询似乎无论如何都希望控制重复项以保持某种程度的性能时,为什么需要这种机制,例如,这是任意选择的,是为了尝试并保持一些ANSI SQL兼容性,是否存在技术限制等。不是每个递归都会在每次调用时添加到堆栈中,并在完成后自行移除?如果是这样的话,在我看来,随着时间的推移,它将进行重复数据消除。我不确定这是否是最佳的解决方案性能方面的问题。这意味着,排序和比较必须在每次迭代之后进行。我想说的是,在计算最终结果之后再做这件事在很多情况下(当然不是所有情况下)都更为有效。编辑中的调用堆栈是从过程递归的工作方式派生出来的,当涉及到SQL Server和它的优化器时,我可能是错的。在最后这样做是不一样的。它可以决定终止循环还是无限循环……因此这基本上是SQL Server的技术限制。有趣!顺便说一句,我真的不需要使用工会,这让我觉得很长一段时间是违反直觉的,昨天我终于好奇地研究了原因,结果发现似乎从来没有人讨论过。
CREATE FUNCTION dbo.F ()
RETURNS @R TABLE(n INT PRIMARY KEY WITH (IGNORE_DUP_KEY = ON))
AS
BEGIN
INSERT INTO @R
VALUES (0); --anchor
WHILE @@ROWCOUNT > 0
BEGIN
INSERT INTO @R
SELECT ( N + 1 )%10
FROM @R
END
RETURN
END
GO
SELECT *
FROM dbo.F ()