Find whether graph has a cycle

為{幸葍}努か 提交于 2020-01-15 05:48:25

问题


I want to find out whether it is possible to find cycles in Hierarchical or Chain data with SQL.

E.g. I have following schema: http://sqlfiddle.com/#!3/27269

create table node (
  id INTEGER
 );

create table edges (
   id INTEGER,
   node_a INTEGER,
   node_b INTEGER
);

create table graph (
  id INTEGER,
  edge_id INTEGER);

INSERT INTO node VALUES (1) , (2), (3), (4);
INSERT INTO edges VALUES (1, 1, 2), (2, 2, 3) , (3, 3, 4) , (4, 4, 1);
-- first graph [id = 1] with cycle (1 -> 2 -> 3 -> 4 -> 1)
INSERT INTO graph VALUES (1, 1), (1, 2), (1, 3), (1, 4); 
-- second graph [id =2] without cycle (1 -> 2 -> 3)
INSERT INTO graph VALUES (2, 1), (2, 2), (2, 3); 

In graph table records with same ID belong to one graph.

I need a query that will return IDs of all graphs that have at least one cycle.

So for example above query should return 1, which is the id of the first graph;


回答1:


First, I assume this is a directed graph. An undirected graph has a trivial cycle if it contains a single edge.

The only tricky part to the recursive CTE is stopping when you've hit a cycle -- so you don't get infinite recursion.

Try this:

with cte as (
      select e.object_a, e.object_b, iscycle = 0
      from edges e
      union all
      select cte.object_a, e.object_b,
             (case when cte.object_a = e.object_b then 1 else 0 end) as iscycle
      from cte join
           edges e
           on cte.object_b = e.object_a
      where iscycle = 0
     )
select max(iscycle)
from cte;



回答2:


I wrote SQL query based on @gordon-linoff answer. In some cases I had infinite loop, so I added column with node_path and then I was checking if the current connection had appeared in that column.

This is this script:

create table edges (
   node_a varchar(20),
   node_b varchar(20)
);

INSERT INTO edges VALUES ('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'D'), ('D', 'K'), ('K', 'A')
GO

with cte as (
    SELECT 
          e.node_a
        , e.node_b
        , 0 as depth 
        , iscycle = 0
        , CAST(e.node_a +' -> '+ e.node_b AS varchar(MAX)) as nodes_path
    FROM edges e

    UNION ALL

    SELECT 
          cte.node_a
        , e.node_b
        , depth + 1
        , (case when cte.node_a = e.node_b then 1 else 0 end) as iscycle
        , CAST(cte.nodes_path+' -> '+ e.node_b AS varchar(MAX)) as nodes_path
    FROM cte 
        JOIN edges e ON cte.node_b = e.node_a AND cte.nodes_path NOT LIKE '%' + CAST(cte.node_a+' -> '+ e.node_b AS varchar(500)) + '%' 
    where iscycle = 0 
    )
SELECT * -- max(iscycle)
FROM cte
option (maxrecursion 300) --just for safety :)

I don't know if it is efficient where are millions of records, so if you can see that I could write this query more optimized, please share with your opinion.



来源:https://stackoverflow.com/questions/26006724/find-whether-graph-has-a-cycle

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!