【问题标题】:How to detect cyclical reference in SQL Server Query - SQL Server 2017如何检测 SQL Server 查询中的循环引用 - SQL Server 2017
【发布时间】:2023-04-08 14:01:01
【问题描述】:

我在 SQL Server 2017 中有一个递归 WITH 查询:

;WITH rec AS (
    SELECT
       col1 AS root_order
      ,col1
      ,col2
      ,col3
      ,col4
      ,col5
      ,col6
      ,col7
      ,col8
      ,col9

    FROM
        TableA


    UNION ALL

    SELECT
        rec.root_order,
        TableA.col2,
        TableA.col3,
        TableA.col4,
        TableA.col5,
        TableA.col6,
        TableA.col7,
        TableA.col8,
        TableA.col9,
        rec.the_level

    FROM
        rec
            INNER JOIN TableA on rec.Details = TableA.Orders
    )

    SELECT DISTINCT * FROM rec

这会产生:The statement terminated. The maximum recursion 100 has been exhausted before statement completion. 错误。

我尝试过: OPTION (maxrecursion 0) 让它继续。但是当我这样做时,查询会无限循环,所以这不起作用。

在 Oracle 中,我可以使用 CONNECT BY ROOTCONNECT BY PRIORNOCYCLE,但我知道 SQL Server 中没有类似的东西。所以我找到了this MSDN link,它暗示了某种形式:

with hierarchy
as
(
select
    child, 
    parent, 
    0 as cycle,
    CAST('.' as varchar(max)) + LTRIM(child) + '.' as [path]
from
    #hier
where
    parent is null

union all

select
    c.child,
    c.parent,
    case when p.[path] like  '%.' + LTRIM(c.child) + '.%' then 1 else 0 end as cycle,
    p.[path] + LTRIM(c.child) + '.' as [path]
from
    hierarchy as p
    inner join
    #hier as c
    on p.child = c.parent
    and p.cycle = 0
)
select
    child,
    parent,
    [path]
from
    hierarchy
where
    cycle = 1;
go

用于寻找循环(或避免循环)。我似乎无法接受当前的查询并以这种方式对其进行编辑。如何编辑我当前的 SQL 以执行 MSDN 文章中的循环引用检测?

SQL FIDDLE 中要求的一些示例数据。

【问题讨论】:

  • SQL Server 不实现循环检测,这与 Oracle 或 DB2 不同。除了UNION ALL 之外,PostgreSQL 通过实现UNION 有一种更平淡无奇的方式。您可能需要编写一个过程。
  • 不需要写程序。只需保留迄今为止遇到的完整路径的分隔列表,并检查您是否不打算加入路径中已包含的项目。即您已经找到的方法
  • 我认为我发布的 MSDN(以及其他类似的 dba.stackexchange.com/questions/162298/…)表明这是可能的。我正在努力解决的问题,可能是因为我不能很好地编写 SQL,是如何用我的代码更改他们的示例,这就是我发布的原因,作为在 Stack Overflow 上的 SQL Server 中执行此操作的示例。
  • 你能给我一些示例数据吗?它不需要包含所有列。我只是想确定我了解您的需求。
  • 我可以试试。我需要欺骗它。问题是我不知道数据在哪里是循环,这就是我需要这个查询的原因。我只是有很多列的大桌子。

标签: sql sql-server recursion ssms-2017


【解决方案1】:

我通常做的事情很简单。在锚查询(CTE 的第一部分)中,我在选择列表中包含了一个值“1 AS Level”。然后在底部查询中,我选择 Level + 1 作为 Level,所以我知道我要达到什么深度。然后我可以在底部查询中加入一个健全的子句来限制深度,即 WHERE LEVEL

这是一个基于 AdventureWorks 的示例:

WITH Materials (BillOfMaterialsID, ProductName, ProductAssemblyID, ComponentID, [Level])
AS
(
    SELECT bom.BillOfMaterialsID, 
           p.[Name], 
           bom.ProductAssemblyID, 
           bom.ComponentID, 
           1
    FROM Production.BillOfMaterials AS bom
    INNER JOIN Production.Product AS p
    ON bom.ComponentID = p.ProductID 
    AND bom.EndDate IS NULL
    WHERE bom.ProductAssemblyID IS NULL

    UNION ALL

    SELECT bom.BillOfMaterialsID, 
           p.[Name], 
           bom.ProductAssemblyID, 
           bom.ComponentID, 
           m.[Level] + 1
    FROM Production.BillOfMaterials AS bom
    INNER JOIN Production.Product AS p
    ON bom.ComponentID = p.ProductID 
    INNER JOIN Materials AS m
    ON bom.ProductAssemblyID = BOM.ComponentID 
    WHERE m.[Level] <= 5
)
SELECT m.BillOfMaterialsID, 
       m.ProductName, 
       m.ProductAssemblyID, 
       m.ComponentID, 
       m.[Level]
FROM Materials AS m
ORDER BY m.[Level], m.BillOfMaterialsID;

【讨论】:

  • 这并没有回答如何检测周期的问题。循环可能发生在第一级。
  • 不,你不能在那里有一个循环,因为你不应该在第一个(锚)子句中使用 CTE 本身。那么它怎么会递归(循环)?
  • 您的帖子没有回答如何检测周期,甚至没有使用问题正文中的任何代码。
猜你喜欢
  • 2017-03-27
  • 2023-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-25
  • 2020-01-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多