【问题标题】:PostgreSQL SQL query for traversing an entire undirected graph and returning all edges found用于遍历整个无向图并返回找到的所有边的 PostgreSQL SQL 查询
【发布时间】:2016-05-22 14:07:36
【问题描述】:

我的 PostgreSQL 数据库中有一个 edges 表,它表示 有向 图的边,有两列:node_fromnode_to(值是节点的 id)。

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

我的意思是,例如下图:

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

如果 initial_nodeabcd,无论如何,我都会得到 [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 并不能消除循环。)

有没有人对处理这个问题有任何建议?

干杯,

安托万。

【问题讨论】:

    标签: postgresql recursion graph traversal


    【解决方案1】:

    我明白了,它不应该与任何类型的数据陷入无限循环:

    --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() 和 ALL() 解决了这个难题。

    【讨论】:

    • 非常感谢 Ziggy !对于最复杂的情​​况,即使对于简单的图表,查询也不会无限循环,它的速度是我之前实现的两倍。
    • 只是为了添加一些东西,在我的情况下,对于初始部分它是: SELECT DISTINCT "to" AS "uniq" FROM edges WHERE "from" = 'initial_node' UNION SELECT DISTINCT "to" AS "uniq " FROM 边缘 WHERE "to" = 'initial_node')
    • 我还必须将 initial_node 添加到数组 (unnsest(points||array('initial_node')) 因为在某些情况下,我不会在结果中得到它。
    猜你喜欢
    • 2011-03-27
    • 2021-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多