【问题标题】:How to Find Circular References in CTE between Parent/Child tables如何在 CTE 中查找父/子表之间的循环引用
【发布时间】:2015-12-17 19:14:49
【问题描述】:

我有一个 CTE 来显示 2 个表(父表和子表)的依赖关系树。 存在导致循环依赖的数据问题,从而引发 Max Recursion level 错误。 即

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

这些表中有数千行。如何编写查询以识别违规参考?或者有没有办法设置最大递归级别,一旦命中就会停止 CTE 而不是抛出错误......然后我可以查看结果并识别问题子项。

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 中使用了扁平的父子表,然后根据 Tab 的答案将其连接到自身,这向我显示了父表和子表中 6 个项目具有相同项目 ID 的位置。 像这样:

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

【问题讨论】:

  • 你能发布你的表结构和当前查询吗?我无法想象您是如何从两个表的父子结构中获取最大递归错误的。
  • @TabAlleman Tab,我添加了表结构。让我知道这是否有意义。
  • @ChadRichardson,当您实际上看到该错误时,您还可以看到选择的结果,您最终会看到哪些行给您带来了问题。
  • @GiorgiNakeuri 感谢 Giorgi,但在 SSMS 中不适合我,结果选项卡没有任何行,并且消息选项卡显示最大递归错误。
  • @ChadRichardson,你能显示查询吗?

标签: sql-server sql-server-2012 common-table-expression


【解决方案1】:

您的 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 WHERE HasCycle = 1 中进行选择,并在 HierarchyItem 中查看开始循环的所有行及其向上的确切路径。

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 你不能这样做。循环引用可能会出现多个级别。
    • 没错,最初的问题让我相信只有两个级别。
    • 但它确实帮助我确定了一些问题,即父表及其直接后代具有相同的 ItemId。但可能还有其他更复杂的循环引用。
    • 你刚刚拯救了我的星期五 :)
    【解决方案3】:

    我以前看到过这些问题,并采取了一次添加一个级别的方法,忽略了以前看到的那些。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-02
      • 1970-01-01
      • 1970-01-01
      • 2016-05-17
      • 1970-01-01
      • 1970-01-01
      • 2018-12-14
      相关资源
      最近更新 更多