为什么Transact-SQL中的递归CTE需要UNION ALL而不是UNION?

Jer*_*acs 6 t-sql sql-server recursion common-table-expression

我知道锚是必要的,这是有道理的.而且我知道需要a UNION ALL,如果你的递归CTE没有,它只是不起作用......但我找不到一个很好的解释为什么会这样.所有文档都只是声明你需要它.

为什么我们不能在递归查询中使用a UNION而不是a UNION ALL?似乎不要在重复递归时包含重复项是个好主意,不是吗?我想,这样的东西应该已经在幕后工作了.

Mar*_*ith 6

我认为原因是他们只是没有认为这是一个值得实施的优先功能.看起来Postgres 确实支持 UNIONUNION ALL.

如果您对此功能有强有力的理由,您可以在Connect(或其替换的任何URL)提供反馈.

防止添加重复项可能很有用,因为在后面的步骤中添加到前一个重复行将几乎总是导致无限循环或超过最大递归限制.

SQL标准中有很多地方使用代码来演示UNION如下所示

在此输入图像描述

本文介绍了如何在SQL Server中实现它们.他们没有做任何类似"引擎盖下"的事情.堆栈假脱机会删除行,因此无法知道后一行是否与已删除的行重复.支持UNION需要一种不同的方法.

与此同时,您可以在多语句TVF中轻松实现相同目标.

下面举一个愚蠢的例子(Postgres Fiddle)

WITH R
     AS (SELECT 0 AS N
         UNION
         SELECT ( N + 1 )%10
         FROM   R)
SELECT N
FROM   R 
Run Code Online (Sandbox Code Playgroud)

更改为UNIONto UNION ALLDISTINCT在末尾添加a 将不会使您免于无限递归.

但你可以实现这一点

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 () 
Run Code Online (Sandbox Code Playgroud)

以上用于IGNORE_DUP_KEY丢弃重复项.如果列列表太宽而无法编入索引,则需要DISTINCTNOT EXISTS不是.您可能还需要一个参数来设置最大递归次数并避免无限循环.

  • 作为旁注,我真的不需要使用UNION,它让我觉得很长时间都是违反直觉的,昨天我终于好奇地研究了为什么,却发现没有人好像在讨论它. (2认同)