我不是SQL专家,但是如果有人可以帮助我。
我使用递归CTE来获取如下值。
Child1 –> Parent 1
Parent1 –> Parent 2
Parent2 –> NULL
如果数据填充出错,那么我将遇到以下类似情况,因此CTE可能会进入无限递归循环并给出最大递归错误。由于数据量很大,因此我无法手动检查此 错误数据 。请让我知道是否有办法找到它。
Parent1 –> Child1
or
Parent1 –> Parent2
Parent2 –> Child1
您尚未指定方言或列名,因此很难给出完美的示例…
-- Some random data IF OBJECT_ID('tempdb..#MyTable') IS NOT NULL DROP TABLE #MyTable CREATE TABLE #MyTable (ID INT PRIMARY KEY, ParentID INT NULL, Description VARCHAR(100)) INSERT INTO #MyTable (ID, ParentID, Description) VALUES (1, NULL, 'Parent'), -- Try changing the second value (NULL) to 1 or 2 or 3 (2, 1, 'Child'), -- Try changing the second value (1) to 2 (3, 2, 'SubChild') -- End random data ;WITH RecursiveCTE (StartingID, Level, Parents, Loop, ID, ParentID, Description) AS ( SELECT ID, 1, '|' + CAST(ID AS VARCHAR(MAX)) + '|', 0, * FROM #MyTable UNION ALL SELECT R.StartingID, R.Level + 1, R.Parents + CAST(MT.ID AS VARCHAR(MAX)) + '|', CASE WHEN R.Parents LIKE '%|' + CAST(MT.ID AS VARCHAR(MAX)) + '|%' THEN 1 ELSE 0 END, MT.* FROM #MyTable MT INNER JOIN RecursiveCTE R ON R.ParentID = MT.ID AND R.Loop = 0 ) SELECT StartingID, Level, Parents, MAX(Loop) OVER (PARTITION BY StartingID) Loop, ID, ParentID, Description FROM RecursiveCTE ORDER BY StartingID, Level
这样的事情将显示递归cte中是否存在循环。看一下专栏Loop。照原样处理数据,没有循环。在注释中,有一些示例,说明了如何更改值以引起循环。
Loop
最后,递归cteVARCHAR(MAX)以|id1|id2|id3|(称为Parents)形式创建一个id ,然后检查当前值ID是否已在该“列表”中。如果是,它将Loop列设置为1。在递归联接(ABD R.Loop = 0)中检查此列。
VARCHAR(MAX)
|id1|id2|id3|
Parents
ID
ABD R.Loop = 0
结束查询使用aMAX() OVER (PARTITION BY ...)将Loop整个“块”链的列设置为1 。
MAX() OVER (PARTITION BY ...)
稍微复杂一点,生成一个“更好”的报告:
-- Some random data IF OBJECT_ID('tempdb..#MyTable') IS NOT NULL DROP TABLE #MyTable CREATE TABLE #MyTable (ID INT PRIMARY KEY, ParentID INT NULL, Description VARCHAR(100)) INSERT INTO #MyTable (ID, ParentID, Description) VALUES (1, NULL, 'Parent'), -- Try changing the second value (NULL) to 1 or 2 or 3 (2, 1, 'Child'), -- Try changing the second value (1) to 2 (3, 3, 'SubChild') -- End random data -- The "terminal" childrens (that are elements that don't have childrens -- connected to them) ;WITH WithoutChildren AS ( SELECT MT1.* FROM #MyTable MT1 WHERE NOT EXISTS (SELECT 1 FROM #MyTable MT2 WHERE MT1.ID != MT2.ID AND MT1.ID = MT2.ParentID) ) , RecursiveCTE (StartingID, Level, Parents, Descriptions, Loop, ParentID) AS ( SELECT ID, -- StartingID 1, -- Level '|' + CAST(ID AS VARCHAR(MAX)) + '|', '|' + CAST(Description AS VARCHAR(MAX)) + '|', 0, -- Loop ParentID FROM WithoutChildren UNION ALL SELECT R.StartingID, -- StartingID R.Level + 1, -- Level R.Parents + CAST(MT.ID AS VARCHAR(MAX)) + '|', R.Descriptions + CAST(MT.Description AS VARCHAR(MAX)) + '|', CASE WHEN R.Parents LIKE '%|' + CAST(MT.ID AS VARCHAR(MAX)) + '|%' THEN 1 ELSE 0 END, MT.ParentID FROM #MyTable MT INNER JOIN RecursiveCTE R ON R.ParentID = MT.ID AND R.Loop = 0 ) SELECT * FROM RecursiveCTE WHERE ParentID IS NULL OR Loop = 1
该查询应返回所有“最后一个孩子”行以及完整的父链。如果没有循环,则为该列Loop;0如果有循环,1则为该列。
0
1