我们有一个由边表表示的有向图。如何在纯SQL中检测周期?
CREATE TABLE edges(id integer primary key identity, from_node int, to_node int);
CREATE NONCLUSTERED INDEX index_edges_of2 ON edges(from_node);
INSERT INTO edges(from_node,to_node) VALUES(1,2),(2,3),(3,1);
答案 0 :(得分:1)
WITH cte AS (
SELECT CAST('.1.' AS varchar(1000)) AS gpath, 1 AS current_node, 0 AS Cycle
UNION ALL
SELECT CAST(CONCAT(gpath, '.', e.to_node, '.') AS varchar(1000)) AS gpath, e.to_node AS current_node, 0 AS cycle
FROM cte
JOIN edges AS e ON e.from_node = cte.current_node
WHERE CHARINDEX(CONCAT('.',e.to_node,'.'),cte.gpath)=0 AND cte.Cycle=0
UNION ALL
SELECT CAST(CONCAT(gpath, '.', e.to_node, '.') AS varchar(1000)) AS gpath, e.to_node AS current_node, 1 AS cycle
FROM cte
JOIN edges AS e ON e.from_node = cte.current_node
WHERE CHARINDEX(CONCAT('.',e.to_node,'.'),cte.gpath)>0 AND cte.Cycle=0
)
SELECT * FROM cte;
gpath current_node周期
.1。 1 0
.1..2。 2 0
.1..2..3。 3 0
.1..2..3..1。 1 1
答案 1 :(得分:1)
解决方案是递归CTE。但是,要使其正常工作,您需要保留已访问节点的列表。 SQL Server对此没有很好的解决方案(例如数组),因此您需要使用字符串操作。
以下将在图中列出循环:
with cte as (
select from_node, to_node,
convert(varchar(max), concat(',', from_node, ',', to_node, ',')) as nodes, 1 as lev,
(case when from_node = to_node then 1 else 0 end) as has_cycle
from edges e
union all
select cte.from_node, e.to_node,
convert(varchar(max), concat(cte.nodes, e.to_node, ',')), lev + 1,
(case when cte.nodes like concat('%,', e.to_node, ',%') then 1 else 0 end) as has_cycle
from cte join
edges e
on e.from_node = cte.to_node
where cte.has_cycle = 0
)
select *
from cte
where has_cycle = 1;
Here是db <>小提琴。