PostgreSQL SQL查询,用于遍历整个无向图并返回找到的所有边

时间:2016-05-22 14:07:36

标签: postgresql recursion graph traversal

我的PostgreSQL数据库中有一个 edge 表,表示定向图的边缘,有两列: node_from node_to (值是节点的id)。

给定一个节点( initial_node )我希望能够遍历整个图形,但是采用无向方式。

我的意思是,例如下图:

(a->b)
(c->b)
(c->d)

如果 initial_node a b c d ,无论如何,我会得到[ a b c d ]。

我使用了以下SQL查询(基于http://www.postgresql.org/docs/8.4/static/queries-with.html):

WITH RECURSIVE search_graph(uniq, depth, path, cycle) AS (
        SELECT
          CASE WHEN g.node_from = 'initial_node' THEN g.node_to ELSE g.node_from END,
          1,
          CASE WHEN g.node_from = 'initial_node' THEN ARRAY[g.node_from] ELSE ARRAY[g.node_to] END,
          false
        FROM edges g
        WHERE 'initial_node' in (node_from, node_to)
      UNION ALL
        SELECT
          CASE WHEN g.node_from = sg.uniq THEN g.node_to ELSE g.node_from END,
          sg.depth + 1,
          CASE WHEN g.node_from = sg.uniq THEN path || g.node_from ELSE path || g.node_to END,
          g.node_to = ANY(path) OR g.node_from = ANY(path)
        FROM edges g, search_graph sg
        WHERE sg.uniq IN (g.node_from, g.node_to) AND NOT cycle
)
SELECT * FROM search_graph

它工作得很好......直到我有一个12个节点全部连接在一起的情况,在所有方向(对于每对我都有(a-> b)和(b-> a)),这使得查询无限循环。 (将UNION ALL更改为UNION不会消除循环。)

有没有人提出任何建议来处理这个问题?

干杯,

安托。

1 个答案:

答案 0 :(得分:0)

我得到了它,不应该使用任何类型的数据进入无限循环:

--create temp table edges ("from" text, "to" text);
--insert into edges values ('initial_node', 'a'), ('a', 'b'), ('a', 'c'), ('c', 'd');

with recursive graph(points) as (
  select array(select distinct "to" from edges where "from" = 'initial_node')
  union all
  select g.points || e1.p || e2.p
  from graph g
  left join lateral (
    select array(
      select distinct "to"
      from edges 
      where "from" =any(g.points) and "to" <>all(g.points) and "to" <> 'initial_node') AS p) e1 on (true)
  left join lateral (
    select array(
      select distinct "from"
      from edges 
      where "to" =any(g.points) and "from" <>all(g.points) and "from" <> 'initial_node') AS p) e2 on (true)
  where e1.p <> '{}' OR e2.p <> '{}'
  )
select distinct unnest(points)
from graph
order by 1

递归查询在可选择的内容方面非常有限,并且因为它们不允许在子选择内使用递归结果,所以不能使用NOT IN(select * from recursive where ...)。使用LEFT JOIN LATERAL存储结果并使用= ANY()和&lt;&gt; ALL()解决了这个难题。