如何在 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
.
中向上的确切路径
我以前见过这些问题,并且一次添加一个级别的项目,忽略以前看到的那些。
我有一个 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
.
我以前见过这些问题,并且一次添加一个级别的项目,忽略以前看到的那些。