2015-12-17 21 views
0

我有一個CTE在兩個表(父表和子表)上顯示依賴關係樹。 存在導致循環依賴的數據問題,導致引發最大遞歸級別錯誤。 即如何在父/子表之間的CTE中查找循環引用

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 

UPDATE

標籤的回答我指出了正確的方向。我在vw_BOM中使用了扁平的父子表,然後根據標籤的答案將其加入到自身中,這顯示出6個項目在父表和子表中具有相同的項目標識。 像這樣:

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 
+0

你可以發佈你的表結構和當前查詢嗎?我無法想象如何從一個雙表父子結構中獲得最大遞歸錯誤。 –

+0

@TabAlleman Tab,我添加了表結構。讓我知道這是否合理。 –

+0

@ChadRichardson,當你看到那個錯誤時,你也可以看到select的結果,最後你會看到哪些行給你提出問題。 –

回答

2

你的CTE已經與項目ID通路級聯層次。如何使用它來確定項目是否已被看到?

向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向上。

0

簡單的自加入應該這樣做:

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

你不能這樣做。循環參考可以通過幾個級別出現。 –

+0

的確,最初的問題讓我相信只有兩個層次。 –

+0

但它確實幫助我確定了一些問題,其中父表和它的直接後代具有相同的ItemId。但可能還有其他更復雜的循環引用。 –