用守卫停止递归 CTE

Stopping recursive CTE with a guard

我有以下从源到目标的分层数据,我想在出现对现有源的引用时停止

create table #temp (source int, destination int);
insert into #temp values (1,3), (3,7), (7,9), (9,1);

WITH cte (Source, Destination, Level, Sources)
AS 
(
    SELECT Source, Destination, 0 AS Level, CAST(Source AS VARCHAR(MAX)) + ',' AS Sources
    FROM #temp 
    WHERE [Source] = 1

    UNION ALL

    SELECT t.[Source], t.Destination, cte.[Level] + 1, cte.Sources + CAST(t.Source AS VARCHAR(MAX)) + ','
    FROM #temp t        
        INNER JOIN cte ON cte.Destination = t.[Source] AND (CAST(t.Destination AS VARCHAR(MAX)) + ',' NOT LIKE '%' + cte.Sources + '%')
)
select * from cte

drop table #temp;

然而,当 运行 这个时,我仍然得到最大递归错误。我应该如何正确编写保护条款?我想要的是前 3 个结果。

在 CTE 的递归部分添加 where 子句。

create table #temp (source int, destination int);
insert into #temp values (1,3), (3,7), (7,9), (9,1);

WITH cte (Source, Destination, Level, Sources)
AS 
(
    SELECT Source, Destination, 0 AS Level, CAST(Source AS VARCHAR(MAX)) + ',' AS Sources
    FROM #temp 
    WHERE [Source] = 1

    UNION ALL

    SELECT t.[Source], t.Destination, cte.[Level] + 1, cte.Sources + CAST(t.Source AS VARCHAR(MAX)) + ','
    FROM #temp t        
        INNER JOIN cte ON cte.Destination = t.[Source] AND (CAST(t.Destination AS VARCHAR(MAX)) + ',' NOT LIKE '%' + cte.Sources + '%')
    where cte.Level < 2 -- stop after 3 levels (0,1,2)
)
select * from cte

drop table #temp;

Fiddle

这里,在CTE的递归成员中:

INNER JOIN cte 
    ON  cte.Destination = t.[Source] 
    AND (CAST(t.Destination AS VARCHAR(MAX)) + ',' NOT LIKE '%' + cte.Sources + '%')

您的 LIKE 操作数是错误的。相反:

INNER JOIN cte 
    ON cte.Destination = t.[Source] 
    AND cte.Sources NOT LIKE CONCAT('%', t.Destination, '%')

即:目的地应该属于已经访问过的来源列表。请注意,使用 CONCAT() 会强制将数字转换为字符串,从而缩短表达式。

Demo on DB Fiddle