如何使用递归查询向后遍历分层树结构

2024-04-01

我使用 PostgreSQL 9.1 来查询分层树结构数据,其中包含与节点连接的边(或元素)。这些数据实际上是针对流网络的,但我已将问题抽象为简单的数据类型。考虑这个例子tree桌子。每条边都有长度和面积属性,用于确定网络中的一些有用的度量。

CREATE TEMP TABLE tree (
  edge text PRIMARY KEY,
  from_node integer UNIQUE NOT NULL, -- can also act as PK
  to_node integer REFERENCES tree (from_node),
  mode character varying(5), -- redundant, but illustrative
  length numeric NOT NULL,
  area numeric NOT NULL,
  fwd_path text[], -- optional ordered sequence, useful for debugging
  fwd_search_depth integer,
  fwd_length numeric,
  rev_path text[], -- optional unordered set, useful for debugging
  rev_search_depth integer,
  rev_length numeric,
  rev_area numeric
);
CREATE INDEX ON tree (to_node);
INSERT INTO tree(edge, from_node, to_node, mode, length, area) VALUES
 ('A', 1, 4, 'start', 1.1, 0.9),
 ('B', 2, 4, 'start', 1.2, 1.3),
 ('C', 3, 5, 'start', 1.8, 2.4),
 ('D', 4, 5, NULL, 1.2, 1.3),
 ('E', 5, NULL, 'end', 1.1, 0.9);

如下图所示,A-E 代表的边与节点 1-5 连接。空值to_node(Ø)代表结束节点。这from_node总是独一无二的,所以可以充当PK。如果这个网络像一个流域 https://en.wikipedia.org/wiki/Drainage_basin,从上到下,起始支流边为A、B、C,结束流出边为E。

The 的文档WITH http://www.postgresql.org/docs/9.1/static/queries-with.html提供了一个很好的示例,说明如何在递归查询中使用搜索图。因此,为了获取“向前”信息,查询从末尾开始,然后向后进行:

WITH RECURSIVE search_graph AS (
  -- Begin at ending nodes
  SELECT E.from_node, 1 AS search_depth, E.length
    , ARRAY[E.edge] AS path -- optional
  FROM tree E WHERE E.to_node IS NULL

  UNION ALL
  -- Accumulate each edge, working backwards (upstream)
  SELECT o.from_node, sg.search_depth + 1, sg.length + o.length
    , o.edge|| sg.path -- optional
  FROM tree o, search_graph sg
  WHERE o.to_node = sg.from_node
)
UPDATE tree SET
  fwd_path = sg.path,
  fwd_search_depth = sg.search_depth,
  fwd_length = sg.length
FROM search_graph AS sg WHERE sg.from_node = tree.from_node;

SELECT edge, from_node, to_node, fwd_path, fwd_search_depth, fwd_length
FROM tree ORDER BY edge;

 edge | from_node | to_node | fwd_path | fwd_search_depth | fwd_length
------+-----------+---------+----------+------------------+------------
 A    |         1 |       4 | {A,D,E}  |                3 |        3.4
 B    |         2 |       4 | {B,D,E}  |                3 |        3.5
 C    |         3 |       5 | {C,E}    |                2 |        2.9
 D    |         4 |       5 | {D,E}    |                2 |        2.3
 E    |         5 |         | {E}      |                1 |        1.1

以上是有道理的,并且对于大型网络来说可以很好地扩展。例如,我可以看到边缘B距末端 3 条边,前进路径为{B,D,E}从尖端到末端的总长度为3.5。

但是,我无法找出构建反向查询的好方法。也就是说,从每条边开始,累积的“上游”边、长度和面积是多少。使用WITH RECURSIVE,我拥有的最好的是:

WITH RECURSIVE search_graph AS (
  -- Begin at starting nodes
  SELECT S.from_node, S.to_node, 1 AS search_depth, S.length, S.area
    , ARRAY[S.edge] AS path -- optional
  FROM tree S WHERE from_node IN (
    -- Starting nodes have a from_node without any to_node
    SELECT from_node FROM tree EXCEPT SELECT to_node FROM tree)

  UNION ALL
  -- Accumulate edges, working forwards
  SELECT c.from_node, c.to_node, sg.search_depth + 1, sg.length + c.length, sg.area + c.area
         , c.edge || sg.path -- optional
  FROM tree c, search_graph sg
  WHERE c.from_node = sg.to_node
)
UPDATE tree SET
  rev_path = sg.path,
  rev_search_depth = sg.search_depth,
  rev_length = sg.length,
  rev_area = sg.area
FROM search_graph AS sg WHERE sg.from_node = tree.from_node;

SELECT edge, from_node, to_node, rev_path, rev_search_depth, rev_length, rev_area
FROM tree ORDER BY edge;

 edge | from_node | to_node | rev_path | rev_search_depth | rev_length | rev_area
------+-----------+---------+----------+------------------+------------+----------
 A    |         1 |       4 | {A}      |                1 |        1.1 |      0.9
 B    |         2 |       4 | {B}      |                1 |        1.2 |      1.3
 C    |         3 |       5 | {C}      |                1 |        1.8 |      2.4
 D    |         4 |       5 | {D,A}    |                2 |        2.3 |      2.2
 E    |         5 |         | {E,C}    |                2 |        2.9 |      3.3

我想将聚合构建到递归查询的第二项中,因为每个下游边缘连接到 1 个或多个上游边缘,但是递归查询不允许使用聚合 https://wiki.postgresql.org/wiki/CTEReadme。另外,我知道连接很草率,因为with recursive结果有多个连接条件edge.

反向/向后查询的预期结果是:

 edge | from_node | to_node |  rev_path   | rev_search_depth | rev_length | rev_area
------+-----------+---------+-------------+------------------+------------+----------
 A    |         1 |       4 | {A}         |                1 |        1.1 |      0.9
 B    |         2 |       4 | {B}         |                1 |        1.2 |      1.3
 C    |         3 |       5 | {C}         |                1 |        1.8 |      2.4
 D    |         4 |       5 | {A,B,D}     |                3 |        3.5 |      3.5
 E    |         5 |         | {A,B,C,D,E} |                5 |        6.4 |      6.8

我该如何编写这个更新查询?

请注意,我最终更关心累积准确的长度和面积总计,并且路径属性用于调试。在我的实际情况中,正向路径最多可达数百条,对于大型且复杂的流域,我预计反向路径可达数万条。


更新2:我重写了原始的递归查询,以便所有累积/聚合都在递归部分之外完成。它应该比这个答案的先前版本表现得更好。 这与answer https://stackoverflow.com/a/28759418/1914376来自@a_horse_with_no_name 的类似问题。

  WITH 
    RECURSIVE search_graph(edge, from_node, to_node, length, area, start_node) AS
    (
        SELECT edge, from_node, to_node, length, area, from_node AS "start_node"
        FROM tree
        UNION ALL
        SELECT o.edge, o.from_node, o.to_node, o.length, o.area, p.start_node
        FROM tree o
    JOIN search_graph p ON p.from_node = o.to_node
    )
    SELECT array_agg(edge) AS "edges"
       -- ,array_agg(from_node) AS "nodes"
          ,count(edge) AS "edge_count"
          ,sum(length) AS "length_sum"
          ,sum(area) AS "area_sum"
    FROM search_graph
    GROUP BY start_node
    ORDER BY start_node
;

结果符合预期:

 start_node | edges       | edge_count | length_sum |  area_sum
------------+-------------+------------+------------+------------
  1         | {A}         |          1 |        1.1 |       0.9
  2         | {B}         |          1 |        1.2 |       1.3
  3         | {C}         |          1 |        1.8 |       2.4
  4         | {D,B,A}     |          3 |        3.5 |       3.5
  5         | {E,D,C,B,A} |          5 |        6.4 |       6.8
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用递归查询向后遍历分层树结构 的相关文章

  • cocos2d 2.0-rc2:结束director并重新启动

    我有一款由 cocos2d 驱动的游戏 它使用 UIKit 菜单 所以我只使用一个视图控制器的框架 即游戏本身 而且 它只有一个场景 从cocos2d 2 0开始 director本身就是一个UIViewController子类 所以我只是
  • 调用一个从 AngularJS 表达式本地计算值的函数是不是很糟糕?

    我读了关于使用范围的一些 AngularJS 陷阱的文章 http thenittygritty co angularjs pitfalls using scopes 并且它指出您不应在表达式中使用函数 并且我知道每次框架认为需要时都可能会
  • 在 ASP.NET 中生成新的 SessionId

    登录时我想生成一个新的 SessionId 我已经发现一种有效的解决方案 https stackoverflow com questions 1368403 generating a new asp net session in the c
  • 禁用 ASPNET 身份 2.0 中的用户

    我正在寻找一种方法来禁用用户而不是从系统中删除它们 这是为了保持相关数据的数据完整性 但似乎 ASPNET 身份只提供删除帐户 有一个新的锁定功能 但似乎可以控制锁定以禁用用户 但只有在尝试了一定次数的错误密码后才将用户锁定 还有其他选择吗
  • 按照说明后“找不到您尝试购买的商品”

    所以我按照以下说明进行操作http developer android com google play billing billing admin html http developer android com google play bi
  • 如何将函数导入到Vue组件中?

    我正在尝试将单个函数导入到我的 Vue 组件中 我为我的函数创建了一个单独的 js 文件 randomId js exports randomId gt My function 在我的 Vue 组件中 我导入了 Random js let
  • Pandas:数据帧累积和,如果其他列为假则重置[重复]

    这个问题在这里已经有答案了 我有一个包含 2 列的数据框 这里的目标很简单 如果行列设置为 False 则重置 df cumsum df value condition 0 1 1 1 2 1 2 3 1 3 4 0 4 5 1 想要的结果
  • 什么时候使用静态库需要头文件?

    如果我在 Linux 中用 C 创建一个静态库并生成 a 文件 我 或其他人 如何使用该库 例如 我的库定义了一个类 我认为仅仅提供 a 文件是不够的 还需要提供头文件 我如何知道 a 文件必须提供哪些头文件 例如 我是否需要提供我的库代码
  • 如何将 char 转换为 unsigned int?

    我有一个字符数组 它实际上用作字节数组 而不是用于存储文本 在数组中 有两个特定字节表示我需要存储到无符号 int 值中的数值 下面的代码解释了设置 char bytes bytes 2 bytes 0 0x0C For the sake
  • 我可以在 Android Market 上出售我的 SL4A 应用程序吗?

    Closed 这个问题是无关 help closed questions 目前不接受答案 我想使用 SL4A 在 Android 上使用 Python 开发一个应用程序 并且我想知道是否可以将其作为应用程序在 Android Market
  • Mysql案例不工作

    SELECT SQL CALC FOUND ROWS a zn name AS zone name c name AS carrier name CASE type WHEN type 1 THEN General day ELSE Spe
  • 当从 HDFS 手动删除分区数据时,如何更新 Hive 中的分区元数据

    自动更新Hive分区表元数据的方法是什么 如果新的分区数据被添加到HDFS 不执行alter table添加分区命令 然后我们可以通过执行命令 msck Repair 来同步元数据 如果从HDFS中删除了大量分区数据 没有执行alter t
  • RecyclerView 不调用 onCreateViewHolder 或 onBindView

    没有收到任何错误 所有数据似乎都有效 由于某种原因 没有调用与视图相关的方法 我已确定以下事项 getItemCount 是唯一被调用的适配器方法 并且返回一个正整数值 我知道这将是你们将要查看的区域 构造函数正在被调用 成员变量有效 Pa
  • 设置 Silex Bootstrap 时出现 Apache 错误:无法检查 htaccess 文件

    我正在尝试使用 Silex Bootstrap 建立一个网站 我已将它与其他 Web 项目一起放在我的文件夹中 并更改了 Apache 配置中的 DocumentRoot
  • 将 scanf 与 NSString 一起使用

    我希望用户输入一个字符串 然后将输入分配给 NSString 现在我的代码如下所示 NSString word scanf s word The scanf http www cplusplus com reference clibrary
  • 如何在 Xcode 10 中恢复快速帮助?

    在我升级到 Xcode 10 后 快速帮助信息仅提供所选类或结构的声明 是否有某个设置可以使其与 Xcode 9 中的设置相同 升级后我遇到了同样的问题 其中函数签名是单击选项时唯一显示的内容 当我删除里面的所有内容后 快速帮助再次出现 L
  • Docker-compose:npm 安装成功后卷中不存在 node_modules

    我有一个具有以下服务的应用程序 web 在端口 5000 上保存并运行 python 3 Flask Web 服务器 使用 sqlite3 worker 有一个index js文件是队列的工作人员 Web 服务器通过端口使用 json AP
  • 将 Python 3 与 AWS lambda 结合使用

    可以在 lambda 中使用使用 Python3 构建的应用程序 而不仅仅是 python2 7 可能会考虑周围的选择 https gun io blog announcing zappa serverless python aws lam
  • Cakephp - CSRF 令牌不匹配

    我在 Cakephp 3 6 中有一个项目 其中 MessageController 中的 3 个操作由 Ajax 调用 但是 我有一个问题 当我向其中一个操作发送请求时 XHR 会向我返回以下内容 message CSRF token m
  • java中的回调是什么[重复]

    这个问题在这里已经有答案了 可能的重复 什么是回调函数 https stackoverflow com questions 824234 what is a callback function 我已经阅读了回调的维基百科定义 但我仍然没有明

随机推荐

  • Android 内存不足错误位图大小超出 2.3.3 中的 vm 预算

    我知道这个问题被问过几次 他们都不清楚解决方案 让我解释一下这个问题 我有一个 Activity 一次加载 4 个图像 我在 onResume 方法中加载图像 加载时活动抛出位图错误 Notes 我使用 setImageResource R
  • 反序列化时使用父对象的属性来确定子类?

    children o kind t3 data ExampleNodeT3 class should be used for kind t3 t3var1 val1 t3var2 true o kind t4 data ExampleNod
  • Android gradle build System.getEnv("RELEASE_PASSWORD") 返回 null

    我遇到了 System getenv 为环境变量返回 null 的问题 我的密码存储在RELEASE PASSWORD环境变量 当我做 echo RELEASE PASSWORD 它打印出正确的值 所以我知道变量已设置 我最初是设置sign
  • 为什么 strftime('%G', strtotime('2017-01-01')) 会产生 2016 年?

    我发现 PHP 5 6 中可能存在错误 功能strftime参数为 G生成 4 位数年份 然而 喂食时似乎返回了错误的年份1483246800 即 2017 年 1 月 1 日 它会返回 2016 年 示例代码片段 echo strftim
  • 可以接受从现有对象实例化吗?

    我偶然发现了这一点 想知道这是否是预期的行为 Interactive shell php gt class dog php public name doggy php public function speak php echo bark
  • 关闭 eclipse 中选定的 HTML 错误

    我最近将 Eclipse 升级到了 Ganymede 版本 3 4 2 现在 我的 JSP 中的 HTML 出现了大量错误 例如没有引号的参数值和缺少结束标记等 这些页面工作得很好 因为我省略这些内容的情况是它们是可选的 我们可以争论是否应
  • python:运行一个超时进程并捕获stdout、stderr和退出状态[重复]

    这个问题在这里已经有答案了 可能的重复 带有超时的子进程 https stackoverflow com questions 1191374 subprocess with timeout 在 Python 中执行以下操作的最简单方法是什么
  • 保持复选框处于选中状态

    使用 Angular 9 和一些自定义输入 我做了以下 gt https stackblitz com edit angular ivy rgsatp https stackblitz com edit angular ivy rgsatp
  • apache POI 中的自动换行(Excel)

    我有一个java程序 它将标头和数据作为输入并生成一个excel文件 然而 有时当标题值很长且列数较多时 我的 Excel 工作表往往会变得不必要的宽 由于标题的原因 我必须向右向下滚动才能看到尾部列的内容 有没有一种方法可以解决这个问题
  • 将 div 链接到 HTML 中不同页面上的特定部分

    嘿 我有一个新的困境 我有 3 个 div 就像 3 个盒子一样 每个 div 中都有一个图像和一些写入的文本 我希望当我单击任何框中的任意位置时 它会转到另一页 例如如果我在主页上 然后单击框 它将转到网站中的 service html
  • 使用带有秘密的 github 操作来构建/部署 React 应用程序

    我正在尝试使用带有秘密的 github 操作来完成构建 部署我的使用 Firebase 目前仅身份验证模块 的 React 应用程序 对于本地开发 我将 env 文件与 webpack 和 dotenv webpack 库一起使用 在本地机
  • 如何使用 python 将 .mp3 文件转换为频率和振幅数组?

    我想设计一个神经网络 训练后将 mp3 文件作为输入 然后根据训练 以 1 10 的等级来决定音乐的好坏 但为此 我需要将音频文件转换为波长 频率 振幅和定义音乐所需的所有其他参数的数组 然后使用这些数组作为神经网络的输入 我应该如何解决这
  • 基于多列删除数据框之间的交集

    我有这两个数据框 df test dimension1 id dimension2 id dimension3 id dimension4 id dimension5 id 0 1 1 1 1 1 1 1177314888 23819878
  • 如何提供一种易于使用的高对比度替代柔和的配色方案?

    如何确保网站的颜色主题提供符合以下要求的高对比度替代方案WCAG 2 最低对比度要求 https webaim org articles contrast sc143除非用户想要或需要更高的对比度 否则更喜欢柔和的低对比度主题 我尝试定义一
  • 如何从 Flask 应用程序中的 MySQL 查询返回数据?

    我有以下代码 import flask as fk import MySQLdb import JSONEncoder class SpecializedJSONEncoder JSONEncoder def default o if is
  • Docker 与 Symfony 4 - 无法看到文件更改

    我正在为 Symfony 4 应用程序的开发环境开发 docker 映像 我正在构建它alpine php fpm and nginx 我已经配置了一个应用程序 但即使对于简单的 hello world 应用程序 性能也不是很好 700ms
  • Indy TCP - 循环读取数据

    TCP 服务器每 8 毫秒连续发送一次数据帧 我想编写一个能够接收这些数据帧的客户端 Indy 9 中是否有任何程序可以知道缓冲区中是否有可用数据 我当前的程序如下 我正在使用线程 procedure TThreadRead Execute
  • Numpy 用全零填充 4D 单位

    我有一个 4D numpy 数组 但每个元素都是可变大小的 3D 体积 本质上它是一个 3D 体积的 numpy 列表 所以 numpy 数组的形状是 Pdb batch x shape 3 并取元素i在那个列表中 它看起来像这样 Pdb
  • 错误 main.lua:23:尝试索引 upvalue 'Menu' (布尔值)

    我正在尝试用 lua 和 love2d 制作一个主菜单 这是我第一次这样做 遗憾的是没有关于此事的教程 所以我自己尝试了一下 我一直遇到这个错误 我不知道如何解决它 请帮助 完整错误消息 错误main lua 23 尝试索引upvalue
  • 如何使用递归查询向后遍历分层树结构

    我使用 PostgreSQL 9 1 来查询分层树结构数据 其中包含与节点连接的边 或元素 这些数据实际上是针对流网络的 但我已将问题抽象为简单的数据类型 考虑这个例子tree桌子 每条边都有长度和面积属性 用于确定网络中的一些有用的度量