Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >PostgreSQL=>递归查询

PostgreSQL=>递归查询

作者头像
上帝
发布于 2018-06-26 07:12:12
发布于 2018-06-26 07:12:12
2K00
代码可运行
举报
文章被收录于专栏:影子影子
运行总次数:0
代码可运行

PostgreSQL=>递归查询

转载请注明源地址:http://www.cnblogs.com/funnyzpc/p/8232073.html

  距上次博客更新刚好两周,这两周发生了很多,比如:SFTP服务拉取数据,第三方公共平台接口逻辑迁移新框架,新框架(Spring Cloud)上手,公司月报和审计数据获取等等。。。,差不多都有无尽的坑,尤其是最后者,实是折腾人啊

~;牢骚归牢骚,但是事情还是要认真做的,。。。,就目前来看,这些对于我最大的好处就是有助于快速理解公司业务逻辑

;啊哈~,扯完,从这些日子开始抽周末时间学习数据库->PosgreSQL(个人惯称:大象

),遂从本节起说PostgreSQL有关的动西。

  记得在上一家公司的时候做过一个冷门的附加功能,就是把根据传入的部门ID(一个List)查找部门下所有的人员,当时是Oracle数据库配合着Mybatis来做的,中间填过两个坑,一个是Mybatis的forach的参数个数超过1K会报错,导致递归不能查询,另一个坑是Oracle的递归造型稍难,这个。。。我翻了几篇博客写了好几行注释加以理解,希望后来人能明白前人的良苦用心。由于新买MBP 未装Oracle环境,oracle的递归讲解就此略过哈(◡‿◡✿)o~

  首先给出一个测试表(elevel) 关于职称级别的表,一位数的ID是最大分类(英语、计算机、会计),然后子级别的parent_id字段引用父级ID,有些级别比较笼统这里不讨论哈~:

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #f4f4f4; background-color: #000000 } span.s1 { }

testDB=> select * from elevel order by  rpad(id::varchar,5,'0');

  id  |         name         | parent_id

------+----------------------+-----------

    1 | 英语                 |

   11 | 英语专业四八级       |         1

  111 | 英语专业四级         |        11

  112 | 英语专业八级         |        11

   12 | 大学英语三、四、六级 |         1

  121 | 大学英语三级         |        12

  122 | 大学英语四级         |        12

  123 | 大学英语六级         |        12

    2 | 计算机               |

   21 | NCR计算机等级        |         2

  211 | NCR计算机一级        |        21

  212 | NCR计算机二级        |        21

  213 | NCR计算机三级        |        21

  214 | NCR计算机四级        |        21

   22 | IT认证类考试         |         2

  221 | CISCO认证            |        22

  222 | ORACLE认证           |        22

    3 | 会计                 |

   31 | 会计从业证           |         3

   32 | 会计职称             |         3

  321 | 初级职称(助理会计师) |        32

  322 | 中级职称(会计师)     |        32

  323 | 高级职称(高级职称)   |        32

 3231 | 正高级会计师         |       323

 3232 | 副高级会计师         |       323

(25 rows)

  数据造型已经给出了,这里我放出建表语句及测试数据:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1 -- create table 
 2 CREATE TABLE elevel
 3 (
 4    id          integer,
 5    "name"      CHARACTER VARYING (20),
 6    parent_id   integer
 7 );
 8 
 9 -- insert data
10      INSERT INTO elevel (id, "name", parent_id) VALUES (1, '英语', NULL);
11      INSERT INTO elevel (id, "name", parent_id) VALUES (2, '计算机', NULL);
12      INSERT INTO elevel (id, "name", parent_id) VALUES (3, '会计', NULL);
13      INSERT INTO elevel (id, "name", parent_id) VALUES (11, '英语专业四八级', 1);
14      INSERT INTO elevel (id, "name", parent_id) VALUES (111, '英语专业四级', 11);
15      INSERT INTO elevel (id, "name", parent_id) VALUES (112, '英语专业八级', 11);
16      INSERT INTO elevel (id, "name", parent_id) VALUES (121, '大学英语三级', 12);
17      INSERT INTO elevel (id, "name", parent_id) VALUES (122, '大学英语四级', 12);
18      INSERT INTO elevel (id, "name", parent_id) VALUES (12, '大学英语三、四、六级', 1);
19      INSERT INTO elevel (id, "name", parent_id) VALUES (123, '大学英语六级', 12);
20      INSERT INTO elevel (id, "name", parent_id) VALUES (21, 'NCR计算机等级', 2);
21      INSERT INTO elevel (id, "name", parent_id) VALUES (22, 'IT认证类考试', 2);
22      INSERT INTO elevel (id, "name", parent_id) VALUES (211, 'NCR计算机一级', 21);
23      INSERT INTO elevel (id, "name", parent_id) VALUES (212, 'NCR计算机二级', 21);
24      INSERT INTO elevel (id, "name", parent_id) VALUES (213, 'NCR计算机三级', 21);
25      INSERT INTO elevel (id, "name", parent_id) VALUES (214, 'NCR计算机四级', 21);
26      INSERT INTO elevel (id, "name", parent_id) VALUES (221, 'CISCO认证', 22);
27      INSERT INTO elevel (id, "name", parent_id) VALUES (222, 'ORACLE认证', 22);
28      INSERT INTO elevel (id, "name", parent_id) VALUES (31, '会计从业证', 3);
29      INSERT INTO elevel (id, "name", parent_id) VALUES (32, '会计职称', 3);
30      INSERT INTO elevel (id, "name", parent_id) VALUES (321, '初级职称(助理会计师)', 32);
31      INSERT INTO elevel (id, "name", parent_id) VALUES (322, '中级职称(会计师)', 32);
32      INSERT INTO elevel (id, "name", parent_id) VALUES (323, '高级职称(高级职称)', 32);
33      INSERT INTO elevel (id, "name", parent_id) VALUES (3231, '正高级会计师', 323);
34      INSERT INTO elevel (id, "name", parent_id) VALUES (3232, '副高级会计师', 323);
35      COMMIT;

  现在我定一个需求:查询“会计”(id=3)类别下的所有的子记录(包含id=3的记录):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 WITH RECURSIVE le (id,name,parent_id) as 
2 (
3  select id,name,parent_id from elevel where id=3
4  union all
5  select e2.id,e2.name,e2.parent_id from elevel e2,le e3 where e3.id=e2.parent_id 
6 )
7  select * from le order by rpad(id::varchar,5,'0') asc;

查询结果: 

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #f4f4f4; background-color: #000000 } span.s1 { }

  id  |         name         | parent_id

------+----------------------+-----------

    3 | 会计                 |

   31 | 会计从业证           |         3

   32 | 会计职称             |         3

  321 | 初级职称(助理会计师) |        32

  322 | 中级职称(会计师)     |        32

  323 | 高级职称(高级职称)   |        32

 3231 | 正高级会计师         |       323

 3232 | 副高级会计师         |       323

(8 rows)

根据以上查询结果,这里敲黑板,划重点

=>“RECURSIVE” 是PostgreSQL的关键字不是具体存在的表

  =>第一行中的:"(id,name,parent_id)"定义的是虚拟el表的参数,字段的名称可随意,但字段的个数一定要与3~5行中的查询结果的个数一致!

  =>"el"是声明的虚拟表,每次递归一层后都会将本层数据写入el中

  =>第三行中的id=3是需要查询开始层的ID,关键是第五行=>需要将虚拟表“el"表与“elevel”实体表连表查询

  =>特别需要注意的是第三行中的中的where条件(e3.id=e2.parent_id) ,取虚拟表的ID和实体表parent_id连

    这个条件决定了当前递归查询的查询方式(向上查询还是向下查询);

  =>第三行的递归开始查询不可缺少,不然查询报错,个人理解这是PostgreSQL根据首行的记录来递归子记录

好了,需要总结的大概就是这些,至于第七行中的rpad函数是向右补齐的函数,用于排序的需要,读者可以略去order by之后的内容。

  好了,一个简单的递归查询就成了,嗯。。。,如需求同学说:我需要将每条记录的递归结构(path)和层级(depath)的顺序都显示出来。

  遗憾的是PG递归查询本身并没有提供相应的函数和关键字来方便我们的需求,怎么办=>加字段:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 with RECURSIVE le (id,name,parent_id,path,depath) as 
2 (
3  select id,name,parent_id,Array[id] as path,1 as depath from elevel where id=3
4  union all
5  select e2.id,e2.name,e2.parent_id,e3.path||e3.id,e3.depath+1 
6      from elevel e2,le e3 where e3.id=e2.parent_id 
7 )
8 select * from le order by rpad(id::varchar,5,'0') asc;

查询结果:

  id  |         name         | parent_id |     path     | depath

------+----------------------+-----------+--------------+--------

    3 | 会计                 |           | {3}          |      1

   31 | 会计从业证           |         3 | {3,3}        |      2

   32 | 会计职称             |         3 | {3,3}        |      2

  321 | 初级职称(助理会计师) |        32 | {3,3,32}     |      3

  322 | 中级职称(会计师)     |        32 | {3,3,32}     |      3

  323 | 高级职称(高级职称)   |        32 | {3,3,32}     |      3

 3231 | 正高级会计师         |       323 | {3,3,32,323} |      4

 3232 | 副高级会计师         |       323 | {3,3,32,323} |      4

(8 rows)

  嗯~,可以看到查询SQL与之上的查询不同的是第三行中定义了一个"Array[id]" 的递归结构字段,最为和一个“1” 的深度字段,Array函数是PostgreSQL特有的数组函数,读者可以自行查阅资料了解哈( ^)o(^ )~。

  当然以上查询语句满足既已有的需求,想下->如果这里变我最成最初我做过的那个需求(查询部门下的所有人,不含部门记录),该怎么办呢。

  额~,递归本身提供给我们的结果已经趋于完美了,由于官方api并没有提供进一步的方法,这里只有从查询结果着手解决这个问题囖

~

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
with RECURSIVE le (id,name,parent_id,path,depath) as 
(
 select id,name,parent_id,Array[id] as path,1 as depath from elevel where id=3
 union all
 select e2.id,e2.name,e2.parent_id,e3.path||e3.id,e3.depath+1 
     from elevel e2,le e3 where e3.id=e2.parent_id 
)
select * from le l where 0=(select count(1) from le where parent_id=l.id) order by rpad(id::varchar,5,'0') asc;

查询结果:

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #f4f4f4; background-color: #000000 } span.s1 { }

  id  |         name         | parent_id |     path     | depath

------+----------------------+-----------+--------------+--------

   31 | 会计从业证           |         3 | {3,3}        |      2

  321 | 初级职称(助理会计师) |        32 | {3,3,32}     |      3

  322 | 中级职称(会计师)     |        32 | {3,3,32}     |      3

 3231 | 正高级会计师         |       323 | {3,3,32,323} |      4

 3232 | 副高级会计师         |       323 | {3,3,32,323} |      4

(5 rows)

 根据以上查询SQL来看,答案其实很简单,在递归完成后将存在子记录的用where条件过滤掉即可(见查询语句最后一行)

嗯,以上几个例子全部是向下递归查询,下面我展示下向上查询的语句,很简单=>

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1  with RECURSIVE le (id,name,parent_id) as 
2  (
3   select id,name,parent_id from elevel where id=323
4   union all
5   select e2.id,e2.name,e2.parent_id from elevel e2,le e3 where e3.parent_id=e2.id
6  )
7  select * from le order by rpad(id::varchar,5,'0') asc;

查询结果:

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #f4f4f4; background-color: #000000 } span.s1 { }

 id  |        name        | parent_id

-----+--------------------+-----------

   3 | 会计               |

  32 | 会计职称            |        3

 323 | 高级职称(高级职称)   |        32

可以看到与向上查询的查询语句相差不几,关键,关键是=>第5行的where条件,很意外吧,如此小的改动就有查询方向上的变化,个人对此的理解是:

 =>递归向下查询是用虚拟表的id去联结递归表的parent_id

  =>递归向上查询是用虚拟表的parent_id去联结递归表的id

  本人愚钝,目前对于两者的区别发现仅限于此,欢迎读者点拨哈

。。。

~

最后,需要说明的是,在公司业务满足的情况下尽可能用单层查询语句查询,尤其对于层级较少较固定的结构下较为合适,此建议主要针对的是递归的两大问题而言:

 1>递归的查询效率较低,尤其是记录较多层级庞大的记录

  2>若现有记录的层级如有交叉,极容易导致递归死循环,这点尤其要注意

OK, 本节完成,下节开始讲:“窗口函数

现在是:2018-01-21 21:20:50,愿各位晚安,明天要上班哦~

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #f4f4f4; background-color: #000000 } span.s1 { }

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-01-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
MySQL 递归查询实践总结
SELECT id, `name`, parent_id FROM `tb_testcase_suite`
授客
2020/04/10
1.9K0
MySQL 递归查询实践总结
MySql通过父id递归向下查询子节点
不用写存储过程,不用建数据库函数,一段sql就可以实现 不用写存储过程,不用建数据库函数,一段sql就可以实现 不用写存储过程,不用建数据库函数,一段sql就可以实现
鱼找水需要时间
2023/02/16
3.1K0
MySql通过父id递归向下查询子节点
mysql树形结构递归查询
之前一直用的是Oracle,对于树形查询可以使用start with ... connect by 
陈灬大灬海
2018/09/12
9.4K0
mysql树形结构递归查询
SQLServer CTE 递归查询
在TSQL脚本中,也能实现递归查询,SQL Server提供CTE(Common Table Expression),只需要编写少量的代码,就能实现递归查询,递归查询主要用于层次结构的查询,从叶级(Leaf Level)向顶层(Root Level)查询,或从顶层向叶级查询,或递归的路径(Path)。
挽风
2021/04/13
1.8K0
SQLServer CTE 递归查询
商品分类递归查询Tree结构展示
1.先查询出符合条件(符合条件是is_show=1,表示展示)的数据 List<Category> categoryList
全栈程序员站长
2022/06/30
1.1K0
mybatis之collection实现递归查询级联数据
mybatis之collection实现递归查询级联数据 数据库结构: mbatis mapper文件: <!-- 通用查询映射结果 --> <resultMap id="BaseResultMap" type="com.xiepanpan.gmall.pms.entity.ProductCategory"> <id column="id" property="id" /> <result column="parent_id" property="parentId
周杰伦本人
2022/10/25
1.2K0
mybatis之collection实现递归查询级联数据
简化 SQL 递归查询
自引用类型的表结构处理起来比较麻烦,比如“分类”表,通常包括自己的ID和父分类ID,当我们要做父分类路径、子分类路径之类的查询时很不方便,例如我们会使用嵌套查询,或者添加冗余字段来记录分类路径信息,都比较麻烦,有没有简单的办法呢?
dys
2018/10/23
1.2K0
MySQL递归查询 三种实现方式
2.3. 方式三 MySQL 8.0 版本以上 使用 WITH RECURSIVE 实现递归
全栈程序员站长
2022/09/14
11.6K0
MySQL递归查询 三种实现方式
mysql递归查询
注意:只支持单个查询,意思是不可以根据两个或者两个以上的子节点同时查询出所有父节点。我们可以看到,上面参数都是单个值进行递归查询的。 西面提供一个函数支持多个查询
全栈程序员站长
2022/07/02
3.2K0
mysql递归查询
超赞,老外的一种避免递归查询所有子部门的树数据表设计与实现!
点击上方蓝色字体,选择“设为星标” 回复”学习资料“获取学习宝典 文章来源:https://sourl.cn/aCCTwr | 目录 问题来了 查出所有子孙部门 查询子孙部门总数 判断是否叶子节点 要不试试这个方法? 查出所有子孙部门 查询子孙部门总数 判断是否叶子节点 其他基本操作 完结 ---- 通常树形结构的存储,是在子节点上存储父节点的编号来确定各节点的父子关系,例如这样的组织结构: 与之对应的表数据(department): 部门表结构(department) id          部门
猿天地
2022/04/08
2.2K0
超赞,老外的一种避免递归查询所有子部门的树数据表设计与实现!
探索 MySQL 递归查询,优雅的给树结构分页!
递归查询是一种在数据库中处理具有层级结构数据的技术。它通过在查询语句中嵌套引用自身,以实现对嵌套数据的查询。递归查询在处理树状结构、父子关系或层级关系的数据时非常有用。
程序员蜗牛
2024/03/13
1.3K0
探索 MySQL 递归查询,优雅的给树结构分页!
SQL如何实现MYSQL的递归查询,SQL实现MYSQL递归
所周知,目前的mysql版本中并不支持直接的递归查询,但是通过递归到迭代转化的思路,还是可以在一句SQL内实现树的递归查询的。这个得益于Mysql允许在SQL语句内使用@变量。以下是示例代码。
IT小马哥
2020/03/18
5.2K0
MyBatis collection 集合嵌套查询树形节点
MyBatis 是数据持久层框架,支持定制化 SQL、存储过程以及高级映射。尤其强大在于它的映射语句,比如高级映射中的 collection 集合。
二哥聊运营工具
2021/12/17
1.3K0
MyBatis collection 集合嵌套查询树形节点
一道Postgresql递归树题
也是偶然的一次,群友出了一道题考考大家,当时正值疫情最最严重的三月(借口...),披着外套,天气也不是很好(借口...),耐着性子花了5分钟理解了下题, 第一个5分钟...无解,再第二个5分钟。。。无解,还第三个5分钟。。。终究无解(之所以如此可能是题目太吸引我了吧),之后又忙于各种琐事,一直到离职后重新找工作, 一再想想这事儿还是不能再拖了,终于 就到了今天...接下来开始表演了 chapter One:题目 将下表源数据排列成指定顺序(看完题目请先思考几分钟) 源数据 id p_id na
上帝
2020/09/27
5030
MyBatis练习(2)查询所有的一级分类,同时查询二级分类,以及查询二级分类下的所有图书
https://gitee.com/the_efforts_paid_offf/java.git
Maynor
2021/12/07
8540
MyBatis练习(2)查询所有的一级分类,同时查询二级分类,以及查询二级分类下的所有图书
探索MySQL递归查询:处理层次结构数据
在数据库管理中,处理具有层次结构的数据一直是一项常见任务。MySQL的递归查询功能通过公用表表达式(CTE)为处理这类数据提供了便捷的方式。递归查询可以用于管理组织结构、目录树等数据,使您能够轻松地查询任意节点的子节点、父节点或整个路径。
俊才
2024/04/15
1.4K0
探索MySQL递归查询:处理层次结构数据
同事问我MySQL怎么递归查询,我懵逼了...
最近在做的业务场景涉及到了数据库的递归查询。我们公司用的 Oracle ,众所周知,Oracle 自带有递归查询的功能,所以实现起来特别简单。
烟雨星空
2020/08/04
3.2K0
关于docker 意外停止,重新快速启动措施
1. 我们要重启这个镜像,需要知道这个镜像ID,类似这个: 7079ff99e10ac326726a364348853c0e508cad8ce00ae970f3c800f172a40252 那么你可以跳过下面的这个步骤:   1. 我们先找到docker 的存放位置,默认的docker 镜像防止在/var/lib/docekr 位置,但是一般来说var的目录比较小,如果我们存的image镜像比较大的话,可能需要修改默认存储地址,这个在这里不讲.  我们采用 sudo docker info 来查询 sudo
Gxjun
2018/06/14
2.1K0
Oracle递归查询:使用prior实现树操作
oracle树查询的最重要的就是select…start with…connect by…prior语法了。依托于该语法,我们可以将一个表形结构的数据以树的顺序列出来。在下面列述了oracle中树型查询的常用查询方式以及经常使用的与树查询相关的oracle特性函数等,在这里只涉及到一张表中的树查询方式而不涉及多表中的关联等。 1、准备测试表和测试数据 --菜单目录结构表 create table tb_menu(     id number(10) not null, --主键id     title v
汤高
2018/01/11
2.2K0
oracle 笔记
如果你想查看一下数据库的时间该怎么办呢?你需要执行一个 SQL 语句,但是 SQL 语句语法规定需要指定一个表,为此 Oracle 设计了一个只有一行一列的表 DUAL,我们可以使用这个表来执行一些不需要表的 SQL 语句。
Remember_Ray
2020/08/05
4.2K0
相关推荐
MySQL 递归查询实践总结
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档