postgresql 使用递归 with recursive 处理 tree 结构数据

os: ubuntu 16.04
postgresql: 9.6.8

基础数据

机构tree结构表

postgres=# drop table xxorg;
DROP TABLE
postgres=# create table xxorg(id int8,name varchar(100),parentid int8);
CREATE TABLE
postgres=# insert into xxorg(id,name,parentid)values(1,'总部',0);
 insert into xxorg(id,name,parentid)values(10,'A分公司',1),(20,'B分公司',1);
 insert into xxorg(id,name,parentid)values(100,'AA经营部',10),(110,'AB经营部',10),(200,'BA经营部',20);
 insert into xxorg(id,name,parentid)values(1000,'AAA办事处',100),(2000,'BAA办事处',200);

postgres=# \d
        List of relations
 Schema | Name | Type  |  Owner   
--------+------+-------+----------
 public | xxorg | table | postgres
(1 row)
postgres=#
postgres=#
postgres=# select * from xxorg;
  id  |   name    | parentid 
------+-----------+----------
    1 | 总部      |        0
   10 | A分公司   |        1
   20 | B分公司   |        1
  100 | AA经营部  |       10
  110 | AB经营部  |       10
  200 | BA经营部  |       20
 1000 | AAA办事处 |      100
 2000 | BAA办事处 |      200
(8 rows)

用户机构表

postgres=# create table xxuserorg(userid varchar(100),orgid int8);
CREATE TABLE
postgres=# insert into xxuserorg(userid,orgid)values('user1',1),('user2',10),('user3',100),('user3',110),('user4',2000);
INSERT 0 5
postgres=#
postgres=#
postgres=# select * from xxuserorg;
 userid | orgid 
--------+-------
 user1  |     1
 user2  |    10
 user3  |   100
 user3  |   110
 user4  |  2000
(5 rows)

初步写法

向上递归

postgres=# with recursive tmp0 as
(  
SELECT id,name,parentid
FROM xxorg
where id = 200 --初始化节点
union
SELECT t1.id,t1.name,t1.parentid
FROM xxorg t1,  
     tmp0 t0  
where 1=1
  and t1.id=t0.parentid
)  
SELECT id,name,parentid   
FROM tmp0;  

 id  |   name   | parentid 
-----+----------+----------
 200 | BA经营部 |       20
  20 | B分公司  |        1
   1 | 总部     |        0
(3 rows)
	

向下递归

postgres=# with recursive tmp0 as
(  
SELECT id,name,parentid
FROM xxorg
where id = 200 --初始化节点
union
SELECT t1.id,t1.name,t1.parentid
FROM xxorg t1,  
     tmp0 t0  
where 1=1
  and t1.parentid=t0.id  
)  
SELECT id,name,parentid   
FROM tmp0; 

  id  |   name    | parentid 
------+-----------+----------
  200 | BA经营部  |       20
 2000 | BAA办事处 |      200
(2 rows)	

不管是向上递归还是向下递归,符合需求的数据都筛选出来了,只是没有层级,不太直观。
最好有类似oracle 的 tree 结构的功能。

修改后写法

向上递归 修改后

postgres=# with recursive tmp0(id,name,parentid,path,depth) as
(  
SELECT id,name,parentid,array[id] as path,1 as depth
FROM xxorg
where id in (select orgid from xxuserorg where userid='user2') --初始化节点
union
SELECT t1.id,t1.name,t1.parentid,t1.id||t0.path,t0.depth+1 as depth
FROM xxorg t1,  
     tmp0 t0
where 1=1
  and t1.id=t0.parentid
)  
SELECT id,name,parentid,path,depth   
FROM tmp0
order by depth desc,id
;  

向下递归 修改后

postgres=# with recursive tmp0 as
(  
SELECT id,name,parentid,array[id] as path,1 as depth
FROM xxorg
where id in (select orgid from xxuserorg where userid='user2') --初始化节点
union
SELECT t1.id,t1.name,t1.parentid,t0.path||t1.id,t0.depth+1 as depth
FROM xxorg t1,  
     tmp0 t0  
where 1=1
  and t1.parentid=t0.id  
)  
SELECT id,name,parentid,path,depth    
FROM tmp0
order by depth,id
; 
	

整合后的最终sql

with recursive tmp0(pathid,pathname,depth,id,name,parentid) as (  
    --向上递归
	SELECT array[id]::text[] as pathid,array[name]::text[] as pathname,1 as depth,
	       id,name,parentid
	  FROM xxorg
	 where id in (select orgid from xxuserorg where userid='user1') --初始化节点
	union
	SELECT array[t1.id]::text[]||t0.pathid as pathid,array[t1.name]::text[]||t0.pathname as pathname,t0.depth+1 as depth,
	       t1.id,t1.name,t1.parentid
	  FROM xxorg t1,  
		   tmp0 t0
	 where 1=1
	   and t1.id=t0.parentid
), tmp1 (pathid,pathname,depth,id,name,parentid) as (  
	--向下递归
	SELECT array[id]::text[] as pathid,array[name]::text[] as pathname,1 as depth,
	       id,name,parentid
	  FROM xxorg
	 where id in (select orgid from xxuserorg where userid='user1') --初始化节点
	union
	SELECT t0.pathid||array[t1.id]::text[] as pathid,t0.pathname||array[t1.name]::text[] as pathname,t0.depth+1 as depth,
	       t1.id,t1.name,t1.parentid
	  FROM xxorg t1,  
	  	   tmp1 t0  
	 where 1=1
	   and t1.parentid=t0.id  
), tmp2 as ( 
    --涉及到的所有有权限的机构
	--发现 tmp0、tmp1 的 pathid、pathname、depth都毫无意义,可以考虑去掉
	SELECT id,name,parentid   
	  FROM tmp0
	union 
	SELECT id,name,parentid  
	  FROM tmp1
), tmp3 (pathid,pathname,depth,id,name,parentid) as (
	--再次对过滤出的机构向下递归,构成tree
	SELECT array[id]::text[] as pathid,array[name]::text[] as pathname,1 as depth,
	       id,name,parentid
	  FROM tmp2
	 where parentid = 0 --这里一定要为root节点,否则出错
	union
	SELECT t0.pathid||array[t1.id]::text[] as pathid,t0.pathname||array[t1.name]::text[] as pathname,t0.depth+1 as depth,
	       t1.id,t1.name,t1.parentid
	  FROM tmp2 t1,  
	       tmp3 t0
	 where 1=1
	   and t1.parentid=t0.id
)
select '/'||array_to_string(a0.pathid,'/') as pathid,
       '/'||array_to_string(a0.pathname,'/') as pathname,
	   a0.depth,
       a0.id,a0.name,a0.parentid,
	   lpad(a0.name, 2*a0.depth-1+length(a0.name),' ') as tree_name,
	   --原始维护的机构
	   case when a0.id = a1.orgid then '*' else null end as orgid_original,
	   --该节点的子节点自动继承父权限
	   case when position( a1.prefix_orgid in '/'||array_to_string(pathid,'/')||'/' ) >0 then '+' end as orgid_extend
  from tmp3 a0
       left outer join (select distinct '/'||orgid||'/' as prefix_orgid,orgid
	                      from xxuserorg
						 where 1=1
						   and userid='user1'
						   and orgid is not null ) a1
	                on position( a1.prefix_orgid in '/'||array_to_string(a0.pathid,'/')||'/' ) >0
order by '/'||array_to_string(a0.pathid,'/'),a0.depth  
; 

在这里插入图片描述
完美。

PostgreSQL的WITH RECURSIVE语句是一种递归查询方式,可以用于处理需要多层嵌套的查询。它允许在查询中引用自身,从而实现复杂的递归操作。这种语法可以很方便地处理一些常见的场景,比如地区表的递归查询和二级联动效果的实现。 在PostgreSQL中,可以使用WITH RECURSIVE来定义一个递归查询的公共表表达式(CTE)。具体的语法如下所示: ``` WITH RECURSIVE cte_name (column_list) AS ( initial_query UNION [ALL] recursive_query ) SELECT column_list FROM cte_name; ``` 其中,cte_name是递归查询的公共表表达式的名称,column_list是需要返回的列,initial_query是初始查询,recursive_query是递归查询。 举个例子,如果我们需要对一个地区表进行递归查询,可以使用WITH RECURSIVE来实现。以下是一个示例查询的语法: ``` WITH RECURSIVE cte AS ( SELECT id, name FROM regions WHERE id = '002' UNION ALL SELECT regions.id, cte.name || '>' || regions.name FROM regions INNER JOIN cte ON regions.pid = cte.id ) SELECT id, name FROM cte; ``` 这个查询会返回具有二级联动效果的结果,即以指定地区为根节点,递归地查询出与之相关联的地区信息,并将结果按照指定的格式进行拼接。 总的来说,PostgreSQL的WITH RECURSIVE语句提供了一种强大的递归查询方式,可以方便地处理复杂的查询需求,比如地区表的递归查询和二级联动效果的实现。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [SQL袖珍参考手册(第3版)](https://download.csdn.net/download/huzhouhzy/4953371)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [PostgreSQL递归查询(with recursive用法)](https://blog.csdn.net/weixin_40983094/article/details/113833256)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据库人生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值