如何在 Parent/Child 个表之间的 CTE 中查找循环引用

How to Find Circular References in CTE between Parent/Child tables

我有一个 CTE 来显示超过 2 table 秒的依赖关系树(一个 parent 和一个 child table)。 存在导致循环依赖的数据问题,导致抛出 Max Recursion 级别的错误。 即

Table: Parent
Id
ItemId

Table: Child
Id
ParentId
ItemId

Example Circular Ref data
Table: Parent
Id   ItemId
1    A
2    B

Table: Child
Id   ParentId  ItemId
1    1         B
2    2         A

这些 table 中有数千行。我如何编写查询来识别有问题的引用?或者有没有一种方法可以设置最大递归级别,一旦命中就停止 CTE 而不是抛出错误...然后我可以查看结果并确定问题 child。

WITH Recursive_CTE AS 
(
    SELECT        
        ItemId, 
        CAST(ItemDescription AS varchar(100)) AS ItemDescription, 
        Qty, 
        CAST(ParentItemId AS SmallInt) AS ParentItemId, 
        CAST(ItemId AS varchar(100)) AS ParentGroupItemId, 
        CAST('   -' AS varchar(100)) AS LVL, 
        CAST(ItemId AS varchar(100)) AS HierarchyItem, 
        CAST(SKU AS varchar(100)) AS HierarchySKU, 
        CAST(ItemDescription AS varchar(100)) AS HierarchyName, 
        0 AS RecursionLevel
    FROM dbo.vw_BOM AS child
    WHERE (ParentItemId = 0) 
    --and ItemId = @BOMHeaderItemId

    UNION ALL

    SELECT        
        child.ItemId, 
        CAST(parent.LVL + child.ItemDescription AS varchar(100)) AS ItemDescription, 
        child.Qty, 
        CAST(child.ParentItemId AS SmallInt) AS ParentItemId, 
        parent.ParentGroupItemId, 
        CAST('   -' + parent.LVL AS varchar(100)) AS LVL, 
        CAST(parent.HierarchyItem + ':' + CAST(child.ItemId AS varchar(100)) AS varchar(100)) AS HierarchyItem,
        CAST(parent.HierarchySKU + ':' + CAST(child.SKU AS varchar(100)) AS varchar(100)) AS HierarchySKU, 
        CAST(parent.HierarchyName + '/' + CAST(child.ItemDescription AS varchar(100)) AS varchar(100)) AS HierarchyName, 
        parent.RecursionLevel + 1 AS RecursionLevel
    FROM Recursive_CTE AS parent INNER JOIN
            dbo.vw_BOM AS child ON child.ParentItemId = parent.ItemId
)

SELECT        
    Recursive_CTE_1.RecursionLevel, 
    Recursive_CTE_1.ParentGroupItemId, 
    Recursive_CTE_1.ParentItemId, 
    Recursive_CTE_1.ItemId, 
    Recursive_CTE_1.Qty, 
    DATALENGTH(Recursive_CTE_1.LVL) AS LVLLength,
    Recursive_CTE_1.ItemDescription, 
    item.SKU, 
    item.OnHandQty, 
    item.AllocQty, 
    item.AvailableQty, 
    item.ToBeReceivedQty, 
    item.AvailableWFutureQty,   
    Recursive_CTE_1.HierarchyItem, 
    Recursive_CTE_1.HierarchySKU, 
    Recursive_CTE_1.HierarchyName
FROM Recursive_CTE AS Recursive_CTE_1 INNER JOIN
        dbo.vw_ItemInventorySummary AS item ON Recursive_CTE_1.ItemId = item.Id
ORDER BY Recursive_CTE_1.HierarchySKU
option (maxrecursion 200)

风景vw_BOM

SELECT        dbo.BillOfMaterialHeader.Id AS Id, dbo.BillOfMaterialHeader.ItemId AS ItemId, 0 AS ParentItemId, FGItems.SKU AS SKU, FGItems.SKU + N': ' + FGItems.ShortDescription AS ItemDescription, 
                         dbo.BillOfMaterialHeader.Quantity AS Qty
FROM            dbo.BillOfMaterialHeader INNER JOIN
                         dbo.Items AS FGItems ON dbo.BillOfMaterialHeader.ItemId = FGItems.Id
UNION ALL
SELECT        dbo.BillOfMaterialDetail.Id AS Id, dbo.BillOfMaterialDetail.ItemId AS ItemId, BOMHdr.ItemId AS ParentItemId, RMItems.SKU AS SKU, RMItems.SKU + N': ' + RMItems.ShortDescription AS ItemDescription, 
                         dbo.BillOfMaterialDetail.Quantity AS Qty
FROM            dbo.Items AS RMItems INNER JOIN
                         dbo.BillOfMaterialDetail ON RMItems.Id = dbo.BillOfMaterialDetail.ItemId INNER JOIN
                         dbo.BillOfMaterialHeader BOMHdr ON dbo.BillOfMaterialDetail.BillOfMaterialHeaderId = BOMHdr.Id

更新

Tab 的回答为我指明了正确的方向。我在 vw_BOM 中使用了扁平化的 Parent Child table 然后根据 Tab 的回答将其连接到自身,这向我展示了 [= 中 6 个项目具有相同项目 ID 的位置34=] Table 和 Child Table。 像这样:

SELECT        dbo.vw_BOM.SKU AS ParentSKU, vw_BOM_1.SKU AS ChildSKU
FROM            dbo.vw_BOM INNER JOIN
                         dbo.vw_BOM AS vw_BOM_1 ON dbo.vw_BOM.ItemId = vw_BOM_1.ParentItemId AND dbo.vw_BOM.ParentItemId = vw_BOM_1.ItemId

简单的自连接应该可以做到:

SELECT * FROM MyTable t1
INNER JOIN MyTable t2
  ON t1.Parent=t2.Child
  AND t1.Child=t2.Parent 

您的 CTE 已经有一个层次结构,其中 ItemID 路径串联在一起。如何使用它来确定该项目是否已被看过?

将新列添加到 CTE 的锚点部分,HasCycle = Convert(bit, 0)

然后在 CTE 的递归部分,在 WHERE 子句中添加列和条件,如下所示:

...
UNION ALL
SELECT
   ... other columns,
   HasCycle = Convert(bit,
      CASE
          WHEN ':' + parent.HierarchyItem + ':' LIKE
             '%:' + Convert(varchar(100), child.ItemID) + ':%'
          THEN 1
          ELSE 0
      END)
FROM
   ...
WHERE
   ...
   AND parent.HasCycle = 0 --terminate after cycle is found
;

然后您可以从递归 CTE select WHERE HasCycle = 1 中查看开始一个循环的所有行及其在 HierarchyItem.

中向上的确切路径

我以前见过这些问题,并且一次添加一个级别的项目,忽略以前看到的那些。