【问题标题】:SQL Server Recursive CTE for finding all the dependencies用于查找所有依赖项的 SQL Server 递归 CTE
【发布时间】:2018-06-19 01:20:54
【问题描述】:

我有一个包含以下数据的表格。

Road    City
R1      C1
R2      C2
R3      C1
R3      C3
R4      C3
R4      C5
R5      C5

如果 R1 是输入,我需要将 R1、R3、R4 和 R5 作为输出。这是因为 R1 属于 C1,C1 有 R3,R3 也属于 C3,C3 有 R4,同样 R5。

我试图利用 CTE 递归,但无法使其工作。我尝试了存储过程递归调用,但它只有 30 级深。

with tmp1 as (
select ROAD, CITY, 1 as Level from table R1 WHERE ROAD = 1712
UNION ALL
select R2.ROAD, R2.CITY,Level + 1 as Level 
from  tmp1  INNER JOIN table R2 ON tmp1.CITY = R2.CITY and tmp1.ROAD <> R2.ROAD   
)
select * from tmp1  
OPTION (maxrecursion 0)

任何想法都非常感谢!

【问题讨论】:

  • 能否分享您编写的 SP,我们可以尝试改进。谢谢
  • SP 不起作用,因为数据深度超过 30 级。
  • 添加了我正在研究的 CTE

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


【解决方案1】:

如果没有某种中断循环的方式,递归 CTE 将无法工作。其他数据库供应商具有不允许将行添加两次的特定功能。除非在最新版本中添加了某些内容,否则 Microsoft SQL Server 不会。

以下不起作用,因为递归子句引用了 CTE 两次。 (或者它包含一个子查询)

WITH recur AS (SELECT Road, City 
    FROM @Map
    WHERE Road = @StartingRoad
    --
    UNION ALL
    --
    SELECT next.Road, next.City
    FROM @Map next
    INNER JOIN recur
        ON (recur.City = next.City AND recur.Road <> next.Road)
        OR (recur.City <> next.City AND recur.Road = next.Road)
    WHERE NOT EXISTS (SELECT NULL
        FROM recur test
        WHERE test.Road = next.Road AND test.City = next.City))
SELECT *
FROM recur;

消息 253,第 16 级,状态 1,第 36 行 公用表表达式“recur”的递归成员具有多个递归引用。

直接循环是可能的,您可以将其保留在存储过程中:

DECLARE @Map TABLE (Road VARCHAR(2), City VARCHAR(2));
INSERT INTO @Map (Road, City)
VALUES ('R1', 'C1')
    , ('R2', 'C2')
    , ('R3', 'C1')
    , ('R3', 'C3')
    , ('R4', 'C3')
    , ('R4', 'C5')
    , ('R5', 'C5');

DECLARE @StartingRoad VARCHAR(2) = 'R1';

DECLARE @Results TABLE (Road VARCHAR(2), City VARCHAR(2));
INSERT INTO @Results (Road, City)
SELECT Road, City
FROM @Map
WHERE Road = @StartingRoad

WHILE (1=1) BEGIN 
    INSERT INTO @Results (Road, City)
    SELECT next.Road, next.City
    FROM @Map next
    INNER JOIN @Results r
        ON (r.City = next.City AND r.Road <> next.Road)
        OR (r.City <> next.City AND r.Road = next.Road)
    WHERE NOT EXISTS (SELECT NULL
        FROM @Results test
        WHERE test.Road = next.Road AND test.City = next.City);
    IF @@ROWCOUNT = 0
        BREAK;
END;

SELECT DISTINCT Road
FROM @Results

【讨论】:

  • 感谢您的回复。我会试试这个。
  • 存在一些性能问题,因此我对其进行了修改并使其正常工作。非常感谢您的解决方案。 {WHILE (1=1) BEGIN DELETE ##ROUTES OUTPUT DELETED.* INTO ##RESULTS FROM ##ROUTES NEXT LEFT JOIN ##RESULTS R1 ON R1.CITY = NEXT.CITY LEFT JOIN ##RESULTS R2 ON R2.ROAD =不存在的 NEXT.ROAD(从 ##RESULTS 测试中选择 TOP 1 1,其中 TEST.ROAD = NEXT.ROAD 并且 TEST.CITY = NEXT.CITY)并且(R1.CITY 不为 NULL 或 R2.CITY 不为 NULL); IF @@ROWCOUNT = 0 中断; END;}
【解决方案2】:

这可能会让你部分到达那里。我正在做部分笛卡尔连接以生成城市/道路组合,并人为地将递归限制为 20 个级别。我不禁想到有更好的方法。

WITH cte(road,
         city,
         connected_city,
         connected_road)
     AS (
     SELECT a.road,
            a.city,
            b.city AS connected_city,
            b.Road connected_road
     FROM deleteme a
          INNER JOIN deleteme b ON a.City = b.City
     WHERE a.road <> b.road)
     SELECT DISTINCT
            road
     FROM cte;

road
R1
R3
R4
R5

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-27
    • 1970-01-01
    • 1970-01-01
    • 2012-04-23
    • 2014-05-19
    • 1970-01-01
    相关资源
    最近更新 更多