问题 为什么我们不能在递归CTE中使用外连接?


请考虑以下内容

;WITH GetParentOfChild AS
    (
        SELECT 
            Rn = ROW_NUMBER() Over(Order By (Select 1))
            ,row_id AS Parents
            ,parent_account_id  As ParentId 
        FROM siebelextract..account
        WHERE row_id = @ChildId
        UNION ALL
        SELECT 
            Rn + 1
            ,a.row_id as Parents
            ,a.parent_account_id As ParentId    
        FROM siebelextract..account a
        JOIN GetParentOfChild gp on a.row_id = gp.ParentId
    )

SELECT TOP 1 @ChildId = Parents 
FROM GetParentOfChild
ORDER BY Rn DESC

它的作用是,给定任何一个孩子,它将返回根级父母......程序完全正常工作......

出于好奇/实验的缘故,我将JOIN更改为Left Outer Join并报告

消息462,级别16,状态1,过程GetParent,第9行 在递归公用表表达式'GetParentOfChild'的递归部分中不允许外连接。

我的问题是为什么CTE的递归部分不能接受左外连接?它是按设计的吗?

谢谢


6741
2018-02-03 09:53


起源



答案:


是的,这是设计,阅读 定义和使用递归公用表表达式的准则

以下是 不允许 在CTE_query_definition中   递归成员:

  • SELECT DISTINCT
  • 通过...分组
  • HAVING
  • 标量聚合
  • 最佳
  • 左,右,外连接(允许内部连接)
  • 子查询

请注意,如果您的查询通过CTE进行左连接,则自身可以变为无限递归。


7
2018-02-03 10:05



我不认为这个答案或链接实际上回答“为什么”。我想合并两个左连接(到外部表)来控制下一次迭代的方向,例如。无限递归似乎不是放弃所有while循环的好理由。 - crokusek
@crokusek,在之前的cte expresion中准备你的coalesce查询,并将其加入你的递归cte查询中。 - dani herrera
哈,我尝试了它,它运行,整洁的技巧。如果我做得对,我必须将左连接转换为完全外部的cte。它运行但查询计划变得悲惨 - 它在开始rte之前完全计算完全外部(不会完成,取决于大小),失去递归探测其确切所需内容的好处。除非cte结果很小,否则游标将执行。对于某些情况,可能是一个很好的解决方法。 - crokusek


答案:


是的,这是设计,阅读 定义和使用递归公用表表达式的准则

以下是 不允许 在CTE_query_definition中   递归成员:

  • SELECT DISTINCT
  • 通过...分组
  • HAVING
  • 标量聚合
  • 最佳
  • 左,右,外连接(允许内部连接)
  • 子查询

请注意,如果您的查询通过CTE进行左连接,则自身可以变为无限递归。


7
2018-02-03 10:05



我不认为这个答案或链接实际上回答“为什么”。我想合并两个左连接(到外部表)来控制下一次迭代的方向,例如。无限递归似乎不是放弃所有while循环的好理由。 - crokusek
@crokusek,在之前的cte expresion中准备你的coalesce查询,并将其加入你的递归cte查询中。 - dani herrera
哈,我尝试了它,它运行,整洁的技巧。如果我做得对,我必须将左连接转换为完全外部的cte。它运行但查询计划变得悲惨 - 它在开始rte之前完全计算完全外部(不会完成,取决于大小),失去递归探测其确切所需内容的好处。除非cte结果很小,否则游标将执行。对于某些情况,可能是一个很好的解决方法。 - crokusek


你不能使用LEFT JOIN和递归CTE,但你可以使用OUTER APPLY,它应该给出相同的结果。


9
2018-03-14 16:45