postgresql WITH RECURSIVE查询选择最长路径

uyhoqukh  于 2024-01-07  发布在  PostgreSQL
关注(0)|答案(3)|浏览(135)

我是PostgreSQL中WITH RECURSIVE的新手。我有一个相当标准的递归查询,它遵循一个邻接列表。如果我有,例如:
它产生:

1
1,2
1,2,3
1,2,3,4
1,2,3,5
1,2,3,5,6

字符串
我想要的是:

1,2,3,4
1,2,3,5,6


但我不知道如何在Postgres中做到这一点。这似乎是“选择最长的路径”或“选择不包含在另一个路径中的路径”。我可能可以看到如何在其自身上使用join来做到这一点,但这似乎效率很低。
示例查询是:

WITH RECURSIVE search_graph(id, link, data, depth, path, cycle) AS (
   SELECT g.id, g.link, g.data, 1, ARRAY[g.id], false
   FROM graph g
  UNION ALL
   SELECT g.id, g.link, g.data, sg.depth + 1, path || g.id, g.id = ANY(path)
   FROM graph g, search_graph sg
   WHERE g.id = sg.link AND NOT cycle
)
SELECT * FROM search_graph;

ykejflvf

ykejflvf1#

您已经有了一个触手可及的cycle解决方案,只需在最后添加一个 predicate 。
但是调整你的break条件一个级别,目前你追加了一个节点太多:

WITH RECURSIVE search AS (
   SELECT id, link, data, ARRAY[g.id] AS path, (link = id) AS cycle
   FROM   graph g
   WHERE  NOT EXISTS (SELECT FROM graph WHERE link = g.id)

   UNION ALL
   SELECT g.id, g.link, g.data, s.path || g.id, g.link = ANY(s.path)
   FROM   search s
   JOIN   graph g ON g.id = s.link
   WHERE  NOT s.cycle
   )
SELECT *
FROM   search
WHERE cycle;
-- WHERE cycle IS NOT FALSE;  -- alternative if link can be NULL

字符串

  • 还包括像mentioned by @wildplasser这样的启动条件。
  • cycle的初始化条件是(link = id),以捕获快捷方式循环。如果您有CHECK约束来禁止表中的快捷方式循环,则不需要。
  • 具体的实现取决于缺少的细节。
  • 这是假设所有的图都以一个循环或link IS NULL终止,并且在同一个表中有一个从linkid的FK约束。确切的实现取决于缺少的细节。如果link实际上不是一个链接(没有引用完整性),你需要适应...
cqoc49vn

cqoc49vn2#

只需将extra子句添加到最终查询中,如:

WITH RECURSIVE search_graph(id, link, data, depth, path, cycle) AS (
   SELECT g.id, g.link, g.data, 1, ARRAY[g.id], false
    FROM graph g
    -- BTW: you should add a START-CONDITION here, like:
    -- WHERE g.id = 1
    -- or even (to find ALL linked lists):
    -- WHERE NOT EXISTS ( SELECT 13
          -- FROM graph nx
          -- WHERE nx.link = g.id
          -- )
  UNION ALL
     SELECT g.id, g.link, g.data, sg.depth + 1, path || g.id, g.id = ANY(path)
    FROM graph g, search_graph sg
    WHERE g.id = sg.link AND NOT cycle
)
SELECT * FROM search_graph sg
WHERE NOT EXISTS ( -- <<-- extra condition
   SELECT 42 FROM graph nx
   WHERE nx.id = sg.link
    );

字符串
请注意:

  • not exists(...)-子句试图连接与递归联合的第二个 * 分支 * 完全相同的记录。
  • 所以:它们是相互排斥的。
  • 如果它 would 存在,它应该已经被递归查询 * 追加到“列表”中。
3mpgtkmj

3mpgtkmj3#

我不确定这是否应该被认为是丑陋的连接解决方案。

WITH recursive graph (child, parent) AS (
    SELECT 2, 1
    UNION
    SELECT 3, 2
    UNION
    SELECT 4, 2
    UNION
    SELECT 6, 5
    UNION
    SELECT 7, 6
    UNION
    SELECT 6, 7
),
paths (start, node, depth, path, has_cycle, terminated) AS (
    SELECT
        ARRAY[g1.parent],
        false,
        false
    FROM graph g1
    WHERE true
        AND NOT EXISTS (SELECT 1 FROM graph g2 WHERE g1.parent = g2.child)
    UNION ALL
    SELECT
        p.path || g.child,
        g.child = ANY(p.path),
        g.parent is null AS terminated
    FROM paths p
    LEFT OUTER JOIN graph g ON g.parent = p.node
    WHERE NOT has_cycle
)
SELECT * from path WHERE terminated
;

字符串
因此,诀窍是使用terminated列通过使用LEFT OUTER JOIN,然后只选择终止路径。

相关问题