CTE递归部分仅返回一行

时间:2018-11-09 15:16:21

标签: sql-server common-table-expression recursive-query

如果我在CTE的递归部分中没有联接,那么每次递归只能得到一行,为什么?

在SQL Server 2016和Azure SQL数据库上经过测试的代码:

DECLARE @Number TABLE (Number INT);

INSERT INTO @Number (Number)
VALUES (1), (2);

;WITH _cte AS 
(
     SELECT Number
     FROM @Number
     UNION ALL
     SELECT _cte.Number
     FROM _cte
)
SELECT *
FROM _cte
OPTION (MAXRECURSION 2); -- just call recursive part twice to see the issue

结果是,我在每个递归/深度中都得到了数字2

我希望当前行在每次递归中都会重复,因此行数呈指数增长

预期产量

Number
--------------------    
1   -- from anchor
2   
1   -- first recursion
2   
1   -- second recursion
2   
1   
2   

实际输出:

Number
--------------------    
1   -- from anchor
2   
2   -- first recursion  
2   -- second recursion

2 个答案:

答案 0 :(得分:2)

根据一些测试,似乎SQL递归首先从初始查询返回的最后一行开始深度进行。到达MAXRECURSION后,查询会以错误(例如The statement terminated. The maximum recursion 2 has been exhausted before statement completion.)终止,并返回错误之前到达的所有结果。您可以通过在插入语句中插入2,1而不是1,2,或在您的@Number表中插入1,2,3来验证这一点。如果您将1,2,3插入@Number,则返回的结果将是

1  -- From anchor
2  -- From anchor
3  -- From anchor
3  -- 1st recursion from last row of anchor query
3  -- Recursion from the row from the line above this (2nd recursion)
-- Query terminates after MAXRECURSION reached on an item and does not attempt recursion on 1 or 2

通常,当使用递归时,您可以使用where语句中的某些条件或联接中的on条件将CTE连接到自身或其他表,这些条件会限制基于可用数据的递归(而不仅仅是通过相同的元素)。例如(使用与您的问题相同的@Number表):

DECLARE @Number TABLE (Number INT);

INSERT @Number
(
    Number
)
VALUES (1),
(2);
WITH _cte2
AS
    (SELECT 
        Number
        ,0 as 'RecursionCount'
    FROM
        @Number

    UNION ALL
        SELECT
        Number
        ,RecursionCount + 1
    FROM
        _cte2
    WHERE
        RecursionCount <= 1
    )
SELECT * FROM _cte2 OPTION (MAXRECURSION 2);

在上面的示例中,查询将在where语句中限制其自身的递归,并且永远不会达到MAXRECURSION,因此查询将能够完成。您还将注意到,返回结果的顺序证实了我的怀疑,即递归是深度优先,从最后一项开始:

Number  RecursionCount
1       0
2       0
2       1
2       2
1       1
1       2

答案 1 :(得分:0)

我将您的代码更改为:

with 

    _cte as (

        SELECT num, 0 iteration
        FROM @Number

        UNION ALL
        SELECT num, iteration + 1
        FROM _cte
        where iteration <= 3 -- comment this line out and compare

    )

    SELECT *
    FROM _cte
    --OPTION (MAXRECURSION 2); -- comment this line out and compare as well

然后我在注释“ where迭代<= 3”时将结果与结果进行了比较。我花了一段时间才意识到,但是结果的排序不是我期望的。

enter image description here

我认为,当遇到最大循环错误时,或者当您选择切断最大递归的选项时,就会以某种方式切断您将在第7-10行中找到的后者的结果。