1. 程式人生 > >postgresql 遞迴處理/樹形結構處理/層次處理

postgresql 遞迴處理/樹形結構處理/層次處理

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

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='user1') --初始化節點
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
), tmp1 (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='user1') --初始化節點
union
SELECT t1.id,t1.name,t1.parentid,t0.path||t1.id,t0.depth+1 as depth
FROM xxorg t1,  
     tmp1 t0  
where 1=1
  and t1.parentid=t0.id  
), tmp2 as ( 
--涉及到的所有有許可權的機構
SELECT id,name,parentid   
FROM tmp0
union 
SELECT id,name,parentid  
FROM tmp1
), tmp3 (id,name,parentid,path,depth) as (
--再次對過濾出的機構向下遞迴,構成tree
SELECT id,name,parentid,array[id] as path,1 as depth
FROM tmp2
where parentid = 0 --這裡一定要為root節點,否則結果不對
union
SELECT t1.id,t1.name,t1.parentid,t0.path||t1.id,t0.depth+1 as depth
FROM tmp2 t1,  
     tmp3 t0
where 1=1
  and t1.parentid=t0.id
)
select id,name,parentid,
       '/'||array_to_string(path,'/') as path,
	   a0.depth,
	   lpad(a0.name, 2*a0.depth-1+length(a0.name),' ') as tree_name
  from tmp3 a0
order by '/'||array_to_string(path,'/'),a0.depth  
; 

  id  |   name    | parentid |      path      | depth |    tree_name     
------+-----------+----------+----------------+-------+------------------
    1 | 總部      |        0 | /1             |     1 |  總部
   10 | A分公司   |        1 | /1/10          |     2 |    A分公司
  100 | AA經營部  |       10 | /1/10/100      |     3 |      AA經營部
 1000 | AAA辦事處 |      100 | /1/10/100/1000 |     4 |        AAA辦事處
  110 | AB經營部  |       10 | /1/10/110      |     3 |      AB經營部
   20 | B分公司   |        1 | /1/20          |     2 |    B分公司
  200 | BA經營部  |       20 | /1/20/200      |     3 |      BA經營部
 2000 | BAA辦事處 |      200 | /1/20/200/2000 |     4 |        BAA辦事處
(8 rows)

完美。