By digoal
Recursive queries are generally used to deal with social relationships, doctor-patient relationships, student-teacher relationships, families, upstream and downstream sensors, etc.
https://www.postgresql.org/docs/14/queries-with.html#QUERIES-WITH-SEARCH
PostgreSQL recursive query supports depth-first and breadth-first search, using the BREADTH DEPTH keyword syntax.
Note:
Imagine there is a tree root:
Friend Relationship Chain: When uid 1 makes friends with uid 2, lkuid is a friend of uid. Write 1,2:
create table bs (uid int, lkuid int, primary key(uid,lkuid));
insert into bs select * from
(select (random()*10)::int c1, (random()*10)::int c2 from generate_series(1,100)) t
where c1<>c2 on conflict do nothing;
Breadth-First Search:
with recursive tmp as (
select uid, lkuid, 0 as depth, array[uid,lkuid] as path, false as cycle from bs where uid=1
union all
select bs.uid, bs.lkuid, tmp.depth+1, tmp.path||bs.lkuid, bs.lkuid=any(tmp.path)
from bs , tmp
where bs.uid=tmp.lkuid
and not bs.lkuid=any(tmp.path)
)
SEARCH BREADTH FIRST BY uid SET ordercol
select * from tmp order by ordercol;
uid | lkuid | depth | path | cycle | ordercol
-----+-------+-------+--------------------------+-------+----------
1 | 8 | 0 | {1,8} | f | (0,1)
1 | 3 | 0 | {1,3} | f | (0,1)
1 | 7 | 0 | {1,7} | f | (0,1)
1 | 10 | 0 | {1,10} | f | (0,1)
1 | 5 | 0 | {1,5} | f | (0,1)
1 | 2 | 0 | {1,2} | f | (0,1)
1 | 4 | 0 | {1,4} | f | (0,1)
2 | 6 | 1 | {1,2,6} | f | (1,2)
2 | 4 | 1 | {1,2,4} | f | (1,2)
2 | 3 | 1 | {1,2,3} | f | (1,2)
3 | 0 | 1 | {1,3,0} | f | (1,3)
3 | 8 | 1 | {1,3,8} | f | (1,3)
3 | 9 | 1 | {1,3,9} | f | (1,3)
3 | 2 | 1 | {1,3,2} | f | (1,3)
3 | 6 | 1 | {1,3,6} | f | (1,3)
4 | 6 | 1 | {1,4,6} | f | (1,4)
4 | 10 | 1 | {1,4,10} | f | (1,4)
4 | 9 | 1 | {1,4,9} | f | (1,4)
4 | 0 | 1 | {1,4,0} | f | (1,4)
4 | 3 | 1 | {1,4,3} | f | (1,4)
5 | 7 | 1 | {1,5,7} | f | (1,5)
5 | 6 | 1 | {1,5,6} | f | (1,5)
5 | 2 | 1 | {1,5,2} | f | (1,5)
5 | 4 | 1 | {1,5,4} | f | (1,5)
7 | 2 | 1 | {1,7,2} | f | (1,7)
7 | 6 | 1 | {1,7,6} | f | (1,7)
7 | 0 | 1 | {1,7,0} | f | (1,7)
7 | 5 | 1 | {1,7,5} | f | (1,7)
7 | 10 | 1 | {1,7,10} | f | (1,7)
7 | 8 | 1 | {1,7,8} | f | (1,7)
7 | 3 | 1 | {1,7,3} | f | (1,7)
8 | 7 | 1 | {1,8,7} | f | (1,8)
8 | 2 | 1 | {1,8,2} | f | (1,8)
8 | 4 | 1 | {1,8,4} | f | (1,8)
8 | 5 | 1 | {1,8,5} | f | (1,8)
8 | 9 | 1 | {1,8,9} | f | (1,8)
8 | 3 | 1 | {1,8,3} | f | (1,8)
10 | 5 | 1 | {1,10,5} | f | (1,10)
10 | 8 | 1 | {1,10,8} | f | (1,10)
10 | 9 | 1 | {1,10,9} | f | (1,10)
0 | 5 | 2 | {1,7,0,5} | f | (2,0)
Depth-First Search:
with recursive tmp as (
select uid, lkuid, 0 as depth, array[uid,lkuid] as path, false as cycle from bs where uid=1
union all
select bs.uid, bs.lkuid, tmp.depth+1, tmp.path||bs.lkuid, bs.lkuid=any(tmp.path)
from bs , tmp
where bs.uid=tmp.lkuid
and not bs.lkuid=any(tmp.path)
)
SEARCH DEPTH FIRST BY uid SET ordercol
select * from tmp order by ordercol;
uid | lkuid | depth | path | cycle | ordercol
-----+-------+-------+--------------------------+-------+--------------------------------------------
1 | 8 | 0 | {1,8} | f | {(1)}
1 | 7 | 0 | {1,7} | f | {(1)}
1 | 4 | 0 | {1,4} | f | {(1)}
1 | 2 | 0 | {1,2} | f | {(1)}
1 | 5 | 0 | {1,5} | f | {(1)}
1 | 10 | 0 | {1,10} | f | {(1)}
1 | 3 | 0 | {1,3} | f | {(1)}
2 | 4 | 1 | {1,2,4} | f | {(1),(2)}
2 | 3 | 1 | {1,2,3} | f | {(1),(2)}
2 | 6 | 1 | {1,2,6} | f | {(1),(2)}
3 | 8 | 2 | {1,2,3,8} | f | {(1),(2),(3)}
3 | 0 | 2 | {1,2,3,0} | f | {(1),(2),(3)}
3 | 9 | 2 | {1,2,3,9} | f | {(1),(2),(3)}
3 | 6 | 2 | {1,2,3,6} | f | {(1),(2),(3)}
0 | 8 | 3 | {1,2,3,0,8} | f | {(1),(2),(3),(0)}
0 | 5 | 3 | {1,2,3,0,5} | f | {(1),(2),(3),(0)}
0 | 9 | 3 | {1,2,3,0,9} | f | {(1),(2),(3),(0)}
5 | 7 | 4 | {1,2,3,0,5,7} | f | {(1),(2),(3),(0),(5)}
5 | 4 | 4 | {1,2,3,0,5,4} | f | {(1),(2),(3),(0),(5)}
5 | 6 | 4 | {1,2,3,0,5,6} | f | {(1),(2),(3),(0),(5)}
4 | 9 | 5 | {1,2,3,0,5,4,9} | f | {(1),(2),(3),(0),(5),(4)}
4 | 6 | 5 | {1,2,3,0,5,4,6} | f | {(1),(2),(3),(0),(5),(4)}
4 | 10 | 5 | {1,2,3,0,5,4,10} | f | {(1),(2),(3),(0),(5),(4)}
6 | 8 | 6 | {1,2,3,0,5,4,6,8} | f | {(1),(2),(3),(0),(5),(4),(6)}
6 | 10 | 6 | {1,2,3,0,5,4,6,10} | f | {(1),(2),(3),(0),(5),(4),(6)}
8 | 7 | 7 | {1,2,3,0,5,4,6,8,7} | f | {(1),(2),(3),(0),(5),(4),(6),(8)}
8 | 9 | 7 | {1,2,3,0,5,4,6,8,9} | f | {(1),(2),(3),(0),(5),(4),(6),(8)}
7 | 10 | 8 | {1,2,3,0,5,4,6,8,7,10} | f | {(1),(2),(3),(0),(5),(4),(6),(8),(7)}
10 | 9 | 9 | {1,2,3,0,5,4,6,8,7,10,9} | f | {(1),(2),(3),(0),(5),(4),(6),(8),(7),(10)}
.......
3 | 8 | 1 | {1,3,8} | f | {(1),(3)}
3 | 0 | 1 | {1,3,0} | f | {(1),(3)}
3 | 9 | 1 | {1,3,9} | f | {(1),(3)}
3 | 2 | 1 | {1,3,2} | f | {(1),(3)}
3 | 6 | 1 | {1,3,6} | f | {(1),(3)}
0 | 8 | 2 | {1,3,0,8} | f | {(1),(3),(0)}
0 | 9 | 2 | {1,3,0,9} | f | {(1),(3),(0)}
0 | 5 | 2 | {1,3,0,5} | f | {(1),(3),(0)}
5 | 7 | 3 | {1,3,0,5,7} | f | {(1),(3),(0),(5)}
5 | 4 | 3 | {1,3,0,5,4} | f | {(1),(3),(0),(5)}
5 | 2 | 3 | {1,3,0,5,2} | f | {(1),(3),(0),(5)}
5 | 6 | 3 | {1,3,0,5,6} | f | {(1),(3),(0),(5)}
2 | 4 | 4 | {1,3,0,5,2,4} | f | {(1),(3),(0),(5),(2)}
2 | 6 | 4 | {1,3,0,5,2,6} | f | {(1),(3),(0),(5),(2)}
4 | 9 | 5 | {1,3,0,5,2,4,9} | f | {(1),(3),(0),(5),(2),(4)}
4 | 10 | 5 | {1,3,0,5,2,4,10} | f | {(1),(3),(0),(5),(2),(4)}
4 | 6 | 5 | {1,3,0,5,2,4,6} | f | {(1),(3),(0),(5),(2),(4)}
Breadth-first and depth-first are implemented by sorting, which is a trick for sorting the search results; one is the record type
, and the other is the record[]
type.
Breadth-First Search:
record
with recursive tmp as (
select uid, lkuid, 0 as depth, array[uid,lkuid] as path, false as cycle from bs where uid=1
union all
select bs.uid, bs.lkuid, tmp.depth+1, tmp.path||bs.lkuid, bs.lkuid=any(tmp.path)
from bs , tmp
where bs.uid=tmp.lkuid
and not bs.lkuid=any(tmp.path)
)
SEARCH BREADTH FIRST BY uid SET ordercol
select pg_typeof(ordercol), * from tmp limit 1;
pg_typeof | uid | lkuid | depth | path | cycle | ordercol
-----------+-----+-------+-------+-------+-------+----------
record | 1 | 7 | 0 | {1,7} | f | (0,1)
(1 row)
Depth-First Search record[]
:
with recursive tmp as (
select uid, lkuid, 0 as depth, array[uid,lkuid] as path, false as cycle from bs where uid=1
union all
select bs.uid, bs.lkuid, tmp.depth+1, tmp.path||bs.lkuid, bs.lkuid=any(tmp.path)
from bs , tmp
where bs.uid=tmp.lkuid
and not bs.lkuid=any(tmp.path)
)
SEARCH DEPTH FIRST BY uid SET ordercol
select pg_typeof(ordercol), * from tmp limit 1;
pg_typeof | uid | lkuid | depth | path | cycle | ordercol
-----------+-----+-------+-------+-------+-------+----------
record[] | 1 | 7 | 0 | {1,7} | f | {(1)}
(1 row)
Implementation Method of Depth-First Search, Sort:
explain with recursive tmp as (
select uid, lkuid, 0 as depth, array[uid,lkuid] as path, false as cycle from bs where uid=1
union all
select bs.uid, bs.lkuid, tmp.depth+1, tmp.path||bs.lkuid, bs.lkuid=any(tmp.path)
from bs , tmp
where bs.uid=tmp.lkuid
and not bs.lkuid=any(tmp.path)
)
SEARCH DEPTH FIRST BY uid SET ordercol
select * from tmp order by ordercol;
QUERY PLAN
-------------------------------------------------------------------------------------------
Sort (cost=111.18..112.52 rows=537 width=77)
Sort Key: tmp.ordercol
CTE tmp
-> Recursive Union (cost=0.00..76.09 rows=537 width=77)
-> Seq Scan on bs (cost=0.00..1.70 rows=7 width=77)
Filter: (uid = 1)
-> Hash Join (cost=2.28..6.37 rows=53 width=77)
Hash Cond: (bs_1.uid = tmp_1.lkuid)
Join Filter: (bs_1.lkuid <> ALL (tmp_1.path))
-> Seq Scan on bs bs_1 (cost=0.00..1.56 rows=56 width=8)
-> Hash (cost=1.40..1.40 rows=70 width=72)
-> WorkTable Scan on tmp tmp_1 (cost=0.00..1.40 rows=70 width=72)
-> CTE Scan on tmp (cost=0.00..10.74 rows=537 width=77)
(13 rows)
The Scale up and Scale down of PostgreSQL Hash Partition Table
ApsaraDB - January 13, 2022
Xiangguang - December 20, 2021
digoal - July 4, 2019
W.T - March 8, 2021
digoal - December 20, 2021
digoal - June 26, 2019
Alibaba Cloud PolarDB for PostgreSQL is an in-house relational database service 100% compatible with PostgreSQL and highly compatible with the Oracle syntax.
Learn MoreAn online MPP warehousing service based on the Greenplum Database open source program
Learn MoreAn on-demand database hosting service for PostgreSQL with automated monitoring, backup and disaster recovery capabilities
Learn MoreLeverage cloud-native database solutions dedicated for FinTech.
Learn MoreMore Posts by digoal