【问题标题】:Postgres recursive function unique valuesPostgres递归函数唯一值
【发布时间】:2014-02-26 05:50:00
【问题描述】:

我有一个图结构

Node(pid integer)
Neighbor(pid integer, pidn integer)

Node 是微不足道的,我应该说Neighbor 为每个节点存储其邻居列表。这是我正在测试的图表(Neighbor 关系的内容):

PID | PIDN
==========
1   | 2
1   | 3
2   | 1
2   | 3
2   | 4
2   | 5
3   | 1
3   | 2
4   | 2
4   | 6
5   | 2
6   | 4

我想得到一个节点的所有邻居的集合,度数小于一个固定的数,所以我执行以下查询:

WITH RECURSIVE search_graph(root, depth) AS (
        SELECT n.pidn, 1
        FROM node p, neighbor n
        WHERE p.pid = n.pid
        AND p.pid = 1
      UNION
        SELECT nxt.pidn, sg.depth + 1
        FROM neighbor nxt, search_graph sg
        WHERE sg.root = nxt.PID
        AND sg.depth < 3
)
SELECT * FROM search_graph s;

起始节点为 1,最大深度为 3(以防错过)。我得到以下结果:

Node | Depth
============
2    | 1
3    | 1
1    | 2
3    | 2
4    | 2
5    | 2
2    | 2
2    | 3
3    | 3
1    | 3
4    | 3
5    | 3
6    | 3

因为它扩展了每个节点的所有子节点,包括访问过的子节点

0                               1
1               2                               3
2       1    3    4    5                    1       2
3      2 3  1 2  2 6   2                   2 3   1 3 4 5

虽然我想排除访问过的孩子,但生成:

Node | Depth
============
2    | 1
3    | 1
4    | 2
5    | 2
6    | 3

我需要一种方法来将结果添加到 search_graph 仅当节点未被访问时。

【问题讨论】:

  • 你考虑过看看ltree吗?它可能会为您节省一些痛苦。另一种可能性可能是看看您是否可以以 GiST 索引可能能够处理的形式对这些数据进行建模。
  • 在这上面花了几分钟,我不评价能够在递归 CTE 中做到这一点的机会。您需要一种方法来重新访问 search_graph 递归项(EXISTS 子查询、左反连接等)以排除已经访问过的节点,并且递归项上既不允许子查询也不允许左连接。您也不能使用数组轻松绕过它,因为递归术语中不允许使用聚合函数。我认为您必须使用递归 SQL 或 PL/PgSQL 函数或在客户端中编写此代码。 (详情:见已删除答案)
  • 好吧,我看不到已删除的答案...但我仍在寻找使用任何可能方式的解决方案... C/PgPLSQL/SQL ...

标签: sql postgresql recursion graph


【解决方案1】:

您是否阅读过有关 WITH RECURISVE 的 Postgresql 文档?

有一些带有图表的示例,其中一个看起来可以解决您的问题:

如果链接关系包含循环,则此查询将循环。因为我们需要一个“深度” 输出,只是将 UNION ALL 更改为 UNION 不会消除循环。相反,我们需要 识别我们是否在遵循 >links 的特定路径时再次到达同一行。我们在容易循环的查询中添加两列路径和循环:

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;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-07
    • 1970-01-01
    • 1970-01-01
    • 2021-12-26
    • 2018-10-07
    • 2021-03-31
    相关资源
    最近更新 更多