pg数据库 递归查询 树形 with recursive

场景
  • 保险公司有全国保司,下属的省份机构、地市机构,甚至是区县机构。在生产库中使用 ID 和上级 ID 来实现这种层级关系。此时需要制作保司维表方便分析时,需要变更为某全国保司下的各省份、地市、区县机构的垂直扁平化结构
  • 做票据OCR识别时,会对图片A分步骤处理。每个步骤产生一张图片,分别为 A1,A2,A3,再对产生的A1再做处理生成A11,A12等图片。这样不断细化处理,以提高识别的准确度。此时会形成一种类似树形的结构,此时需要统计深度超过3的图片有哪些(N 度搜索)
  • pg数据库中有一张A表且在表上创建了视图B,当想要更新A 表中字段类型时,就会报错存在依赖性视图,无法变更。更有可能在视图B上面又创建了嵌套视图C

方法

在pg语法中使用with recursive语法满足上述场景。官网有详细语法解释

with 语法本身可以用来构建一个公共表达式,这个表达式可以当做临时表一样被查询。加上recursive关键字就可以引用它自己的输出,直到条件结束,这就实现了SQL语法中的递归函数。

具体了解下 with recursive ... 语法使用,官方文档有个例子

WITH RECURSIVE t(n) AS (
    VALUES (1)
  UNION ALL
    SELECT n+1 FROM t WHERE n < 100
)
SELECT sum(n) FROM t;

/*
详细解读下上面SQL。这是将1到100的数值相加的一段递归SQL

1. WITH RECURSIVE 子句:
    这表明要定义一个递归的公共表表达式(CTE Common Table Expression)。

2. t(n) AS (...):
    定义了一个名为't'的CTE,它有一个列名为'n'。

3. 基础部分(非递归部分):
    VALUES (1)
    这是递归的起点,它插入值1作为初始行。

4. UNION ALL:
    这个关键字将基础部分和递归部分的结果合并。也可以使用UNION,那么结果会合并去重。

5. 递归部分:
    SELECT n+1 FROM t WHERE n < 100
    从't'表中选择'n'值
    将'n'加1
    只有当'n'小于100时才继续递归

6. 递归过程:
    第1次迭代:n = 1
    第2次迭代:n = 2
    第3次迭代:n = 3
    ...
    第99次迭代:n = 99
    第100次迭代:n = 100
    当n = 100时,WHERE条件不再满足,递归停止

7. SELECT sum(n) FROM t:
    从生成的't'表中选择所有'n'值,这里的't'表就是刚刚定义的 CTE
    对这些值求和

*/

回归到场景实现

想要实现扁平化结构、深度查询、自身关联都可以使用这种结构,举例如下

-- 查询深度超过 2 的票据
with recursive cte(id, url, parent_img_id, level) as ( --定义一个表名为 cte 的递归公共表达式,后面可以被当做表名一样查询,其有4个字段id, url, parent_img_id, level
	select id, url, cut_f_id as parent_img_id, 1 as level from ods.img where cut_f_id is null --定义非递归查询,起初深度 level 写死 1
	union all
	select t.id, t.url, t.cut_f_id as parent_img_id, cte.level + 1 as level from ods.img t join cte on t.cut_f_id = cte.id --定义递归查询部分,每次 票据id 和 父票据id 关联上则深度level+1
)
select * from cte where cte.level > 2 --从公共表达式中查询深度大于 2 的记录

\

--查询扁平化结构
/* 创建测试表temp_recur,表内容
id    fid    name    level
1             A        1
2      1      A1       2
3      1      A2       2
4      1      A3       2
5      2      A4       3
6      3      A5       3
7      3      A6       3
8      3      A7       3
*/
WITH RECURSIVE tree(id, fid, lvl1_name, lvl2_name, lvl3_name, level) AS (
    -- 基础查询:选择顶级项目(fid 为 null 的项目)
    SELECT id, fid, name, name, name, level FROM dqs.temp_recur WHERE fid IS NULL
    UNION ALL
    -- 递归部分:选择子项目
    SELECT t.id, t.fid,
           CASE WHEN t.level = '2' THEN tree.lvl1_name ELSE tree.lvl1_name END,
           CASE WHEN t.level = '2' THEN t.name ELSE tree.lvl2_name END,
           CASE WHEN t.level = '2' then t.name when t.level = '3' THEN t.name ELSE tree.lvl3_name END,
           t.level
    FROM dqs.temp_recur t
    JOIN tree ON t.fid = tree.id
    WHERE t.level IN ('2', '3')
)
SELECT * FROM tree order by 1

\

结论

用SQL递归公共表达式来实现普通递归场景是很方面的,但是有利也有弊

  • SQL 的性能优势在于数据库引擎的优化,特别是处理大量数据时。
  • 语言的优势在于可以实现更复杂的算法和控制流程。当数据中有很复杂的处理逻辑时,用语言更有优势。

对于简单的递归问题,使用 SQL 的递归公共表表达式(CTE)通常会更快、更简洁。SQL 的递归 CTE 可以高效地生成数据序列。

类似的还有最短路径计算,可以参考德哥博客

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 PostgreSQL 中,可以使用多个子查询语句来构建更复杂的查询逻辑。子查询可以嵌套在其他查询中,并且可以与其他查询语句组合使用,以实现更高级的数据操作和筛选。 下面是一个示例,展示了如何在 PostgreSQL 中使用多个子查询语句: 假设我们有一个名为 "employees" 的表,其中包含员工的信息,包括姓名、部门和工资。我们想要查询所有工资高于部门平均工资的员工,并按照工资从高到低排序。 ```sql SELECT * FROM employees WHERE salary > ( SELECT AVG(salary) FROM employees WHERE department = ( SELECT department FROM employees WHERE name = 'John Doe' ) ); ``` 在上面的示例中,我们使用了三个子查询语句。第一个子查询用于获取部门的平均工资,第二个子查询用于根据给定的名字查找部门,第三个子查询用于筛选出工资高于平均工资的员工。最终的结果集将包含满足条件的所有员工记录,按照工资从高到低排序。 请注意,这只是一个简单的示例,实际应用中的子查询可能更加复杂。您可以使用不同的运算符(如比较运算符、逻辑运算符)和聚合函数来构建更高级的查询逻辑。此外,还可以使用连接(JOIN)和其他聚合操作来合并多个表的数据。 希望这个示例能帮助您理解如何在 PostgreSQL 中使用多个子查询语句。如有需要,请提供更具体的问题或场景,我将尽力为您提供帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值