postgresql递归查询指定搜索顺序的方法

原文地址

搜索顺序

在使用递归查询进行树遍历计算时,你可能希望按照深度优先或广度优先的顺序对结果进行排序。这可以通过在数据列之外再计算一个排序列来实现,并在最后使用该列对结果进行排序。需要注意的是,这实际上并不会控制查询评估访问行的顺序;这个顺序在 SQL 中始终依赖于具体实现。此方法仅提供了一种在事后对结果进行排序的便捷方式。

创建深度优先顺序

我们可以为每个结果行计算一个数组,用于记录截至目前已访问过的行。例如,考虑以下使用 link 字段搜索表 tree 的查询:

sql 复制代码
WITH RECURSIVE search_tree(id, link, data) AS (
    SELECT t.id, t.link, t.data
    FROM tree t
  UNION ALL
    SELECT t.id, t.link, t.data
    FROM tree t, search_tree st
    WHERE t.id = st.link
)
SELECT * FROM search_tree;

要添加深度优先排序信息,可以这样写:

sql 复制代码
WITH RECURSIVE search_tree(id, link, data, path) AS (
    SELECT t.id, t.link, t.data, ARRAY[t.id]
    FROM tree t
  UNION ALL
    SELECT t.id, t.link, t.data, path || t.id
    FROM tree t, search_tree st
    WHERE t.id = st.link
)
SELECT * FROM search_tree ORDER BY path;

在一般情况下,如果需要使用多个字段来标识一行,可以使用行数组。例如,如果需要追踪字段 f1 和 f2:

sql 复制代码
WITH RECURSIVE search_tree(id, link, data, path) AS (
    SELECT t.id, t.link, t.data, ARRAY[ROW(t.f1, t.f2)]
    FROM tree t
  UNION ALL
    SELECT t.id, t.link, t.data, path || ROW(t.f1, t.f2)
    FROM tree t, search_tree st
    WHERE t.id = st.link
)
SELECT * FROM search_tree ORDER BY path;

提示 :在常见的只需追踪一个字段的情况下,可以省略 ROW() 语法。这样可以使用简单数组而非复合类型数组,从而提高效率。

创建广度优先顺序

你可以添加一个列来追踪搜索的深度,例如:

sql 复制代码
WITH RECURSIVE search_tree(id, link, data, depth) AS (
    SELECT t.id, t.link, t.data, 0
    FROM tree t
  UNION ALL
    SELECT t.id, t.link, t.data, depth + 1
    FROM tree t, search_tree st
    WHERE t.id = st.link
)
SELECT * FROM search_tree ORDER BY depth;

为了获得稳定的排序,可以将数据列作为次要排序列添加。

提示:递归查询评估算法会以广度优先搜索的顺序产生输出。然而,这是一个实现细节,依赖它可能并不可靠。每一层内的行顺序肯定是未定义的,因此在任何情况下,可能都需要一些显式的排序。

内置语法

PostgreSQL 提供了内置语法来计算深度优先或广度优先的排序列。例如:

sql 复制代码
WITH RECURSIVE search_tree(id, link, data) AS (
    SELECT t.id, t.link, t.data
    FROM tree t
  UNION ALL
    SELECT t.id, t.link, t.data
    FROM tree t, search_tree st
    WHERE t.id = st.link
) SEARCH DEPTH FIRST BY id SET ordercol
SELECT * FROM search_tree ORDER BY ordercol;
sql 复制代码
WITH RECURSIVE search_tree(id, link, data) AS (
    SELECT t.id, t.link, t.data
    FROM tree t
  UNION ALL
    SELECT t.id, t.link, t.data
    FROM tree t, search_tree st
    WHERE t.id = st.link
) SEARCH BREADTH FIRST BY id SET ordercol
SELECT * FROM search_tree ORDER BY ordercol;

该语法在内部会被扩展为类似于上述手写形式的内容。SEARCH 子句指定了是需要深度优先还是广度优先搜索、用于排序的列列表,以及一个将包含可用于排序的结果数据的列名。该列将隐式添加到 CTE 的输出行中。

相关推荐
2401_891409267 小时前
量化数据13:港股分钟K线、Level2逐笔Tick、十档盘口和港股通历史数据的批量提取
数据库
w-w0w-w14 小时前
SQL注入
网络·数据库·sql
tianyuanwo14 小时前
NetBox 数据库详解:当“关系型数据库”被装进一个网络运维的领域模型里
运维·数据库·netbox
一条泥憨鱼14 小时前
【计算机网络】短连接、长连接与连接池:从 TCP 握手到池化复用的完整链路
网络·数据库·网络协议·tcp/ip·计算机网络
IvorySQL14 小时前
深入 PostgreSQL WAL:从崩溃恢复到逻辑复制
数据库·postgresql
幽灵诶15 小时前
Mysql(基于测试的学习)
数据库·mysql
砚底藏山河15 小时前
python量化自定义周期 K 线合成
java·开发语言·数据库·python·金融
宇宙无敌暴龙战士66616 小时前
MySQL 8 库表操作学习笔记:SQL 基础、库表管理、约束与用户授权全流程
数据库·sql·mysql·学习笔记·后端开发
猿究院_夏燊16 小时前
MySQL 的四种隔离级别
java·数据库·mysql
FungLeo16 小时前
成为全栈·Go 后端篇·从 Node SQLite 到 PostgreSQL:把迁移做成独立交付物
postgresql·golang·sqlite·go·数据库迁移·成为全栈