排除自引用元素递归 cte

exclude self-referencing elements recursive cte

我正在创建一个递归 CTE,因为我需要创建组合的层次结构。

当我 FROM 引用回 TO 时遇到问题。然后它无限循环并达到最大递归限制。我可以在 Levels 上设置一个 where 子句,但是我可能不会得到正确的数据,因为我的树可以是 x levels deeps

我的数据可能是这样的

From    To      Total  Type
98579   10406   82     B
98579   17005   5834   S    
98579   18879   6323   S    
98579   18889   215    S
10406   43594   234    B
10406   73959   10     B
10406   98579   22824  B    
43594   83827   4      S
43594   38475   543    S

正如您在数据中看到的那样,98579 命中 10406 并且 vise verca。

我的 cte 现在看起来像这样:

    ;WITH x AS
(
    -- anchor:
  SELECT b.[From]
      ,b.[To]
      ,b.[Total]
      ,b.[Type]
      ,0 as levels
  FROM [dbo].[Test] b

  where b.[FROM]= 98579
    UNION ALL
    -- recursive:
    SELECT tm.[FROM], tm.[TO], tm.[Total], tm.[Type],levels +1
    FROM  [dbo].[Test] AS tm INNER JOIN  x
    ON x.TO= tm.FROM
    --where levels <= 1
)
SELECT  *FROM x
order by levels

我能做什么?

如果我理解正确的话,你只是想在递归回到开始的地方后终止它。

一个选项是添加一个像 StartPoint 这样的列或任何您想调用的列,然后在 where 子句中使用它来终止递归或过滤掉那些。

在不知道具体你想要的输出是什么的情况下,我根据示例数据假设这就是你想要的,代码中添加了注释:

DECLARE @TestData TABLE
    (
        [From] INT
      , [To] INT
      , [Total] INT
      , [type] CHAR(1)
    );

INSERT INTO @TestData (
                          [From]
                        , [To]
                        , [Total]
                        , [type]
                      )
VALUES ( 98579, 10406, 82, 'B' ) , ( 98579, 17005, 5834, 'S' ) , ( 98579, 18879, 6323, 'S' ) , ( 98579, 18889, 215, 'S' ) , ( 10406, 43594, 234, 'B' ) , ( 10406, 73959, 10, 'B' ) , ( 10406, 98579, 22824, 'B' ) , ( 43594, 83827, 4, 'S' ) , ( 43594, 38475, 543, 'S' );

WITH [x]
AS (
   -- anchor:
   SELECT [b].[From] AS [StartPoint] --Where are we starting
        , [b].[From]
        , [b].[To]
        , [b].[Total]
        , [b].[type]
        , 0 AS [levels]
   FROM   @TestData [b]
   WHERE  [b].[From] = 98579
   UNION ALL
   -- recursive:
   SELECT     [x].[StartPoint] --Add it here
            , [tm].[From]
            , [tm].[To]
            , [tm].[Total]
            , [tm].[type]
            , [x].[levels] + 1
   FROM       @TestData AS [tm]
   INNER JOIN [x]
       ON [x].[To] = [tm].[From]
   WHERE      [x].[StartPoint] <> [tm].[From] --stop the recursion once we have come back to where it started, filter those out.
   )
SELECT   [x].[From]
        , [x].[To]
        , [x].[Total]
        , [x].[type]
        , [x].[levels]
FROM     [x]
ORDER BY [x].[levels];

给出结果:

From        To          Total       type levels
----------- ----------- ----------- ---- -----------
98579       10406       82          B    0
98579       17005       5834        S    0
98579       18879       6323        S    0
98579       18889       215         S    0
10406       43594       234         B    1
10406       73959       10          B    1
10406       98579       22824       B    1
43594       83827       4           S    2
43594       38475       543         S    2

在这个例子中,我包括了你添加过滤器的地方 WHERE [b].[From] = 98579 不清楚这是为了显示循环引用的例子,还是你这样做是为了表明你的起点。

如果您删除上面代码中的 where 子句,它将提供所有内容。基本上每一行都考虑 StartPoint 并且您将获得每一行的所有重复,但是一旦它回到开始的位置就会 stop/filter :

给你:

From        To          Total       type levels
----------- ----------- ----------- ---- -----------
98579       10406       82          B    0
98579       17005       5834        S    0
98579       18879       6323        S    0
98579       18889       215         S    0
10406       43594       234         B    0
10406       73959       10          B    0
10406       98579       22824       B    0
43594       83827       4           S    0
43594       38475       543         S    0
98579       10406       82          B    1
98579       17005       5834        S    1
98579       18879       6323        S    1
98579       18889       215         S    1
43594       83827       4           S    1
43594       38475       543         S    1
10406       43594       234         B    1
10406       73959       10          B    1
10406       98579       22824       B    1
43594       83827       4           S    2
43594       38475       543         S    2