PostgreSQL中WITH的用法
with语句的用法
with查询在复杂查询中定义一个辅助语句(可以理解为在一个查询中定义的临时表),这一特性常用语复杂查询或递归查询应用场景
1.在复杂查询语句中
mydb@localhost:5432=>with t as mydb-> (select generate_series(1, 3)) mydb-> select * from t; generate_series ----------------- 1 2 3 (3 rows)
一开始定义了一条辅助语句取数,之后在主查询语句中查询t,定义的辅助语句就像是定义了一张临时表,对于复杂查询如果不使用CTE(通用表达式),可以通过创建视图方式简化SQL。
下面我们来看一个相对复杂的查询,CTE可以简化SQL并且减少嵌套,因为可以预先定义辅助语句,之后在主查询中多次调用。
with regional_sales as ( select region, sum(amount) as total_sales, from orders group by region ), top_regions as ( select region from regional_sales where total_sales > (select sum(total_sales)/10 from regional_sales) ) select region, product, sum(quantity) as product_utits, sum(amount) as product_sales from orders where region in (select region from top_regions) group by region, product;
定义了regional_sales和top_regions两个辅助语句,regional_sales算出每个区域的总销售额,top_regions算出销售额在总销售额10%以上的所有区域,主查询通过辅助语句与orders表关联,算出顶级区域每件商品的销售额和销售量。
2.递归查询
with查询的一个重要属性是 RECURSIVE
,使用RECURSIVE
可以引用自己的输出,从而实现递归,一般用于层次结构和树状结构中。
先举一个简单的例子
mydb@localhost:5432=>with recursive t(x) as ( mydb(> select 1 mydb(> union mydb(> select x + 1 mydb(> from t mydb(> where x < 5 mydb(> ) mydb-> select sum(x) from t; sum ----- 15 (1 row)
再举一个稍微复杂点的例子,
- 先创建一张表
mydb@localhost:5432=>create table test_area(id int4, name varchar(32), fatherid int4); CREATE TABLE
- 向表里面写入测试数据
mydb@localhost:5432=>insert into test_area values mydb-> (1, '中国', 0), mydb-> (2, '辽宁', 1), mydb-> (3, '山东', 1), mydb-> (4, '沈阳', 2), mydb-> (5, '大连', 2), mydb-> (6, '济南', 3), mydb-> (7, '和平区', 4), mydb-> (8, '沈河区', 4); INSERT 0 8
现在查看下表里面的数据
mydb@localhost:5432=>select * from test_area; id | name | fatherid ----+--------+---------- 1 | 中国 | 0 2 | 辽宁 | 1 3 | 山东 | 1 4 | 沈阳 | 2 5 | 大连 | 2 6 | 济南 | 3 7 | 和平区 | 4 8 | 沈河区 | 4 (8 rows)
上面的数据明显具有层级关系
- 现在我们的需求是这个样子的,给定一个id,可以完整的打印出来完整的地名,比如id=7,需要得到
中国辽宁沈阳和平区
直接展示代码
mydb@localhost:5432=>with recursive r as ( select * from test_area where id=7 union all select test_area.* from test_area, r where test_area.id=r.fatherid ) select * from r order by id; id | name | fatherid ----+--------+---------- 1 | 中国 | 0 2 | 辽宁 | 1 4 | 沈阳 | 2 7 | 和平区 | 4 (4 rows)
接着我们使用string_agg
来合并起来
mydb@localhost:5432=>with recursive r as ( select * from test_area where id=7 union all select test_area.* from test_area, r where test_area.id=r.fatherid ) select string_agg(name, '') from (select name from r order by id) as n; string_agg -------------------- 中国辽宁沈阳和平区 (1 row)
- 入股我们修改下需求,例如给出id,我们需要得到id的地名和下面的所有子地名。以id=4为例
mydb@localhost:5432=>with recursive r as ( mydb(> select * from test_area where id=4 mydb(> union all mydb(> select test_area.* from test_area, r where test_area.fatherid=r.id mydb(> ) mydb-> select * from r order by id; id | name | fatherid ----+--------+---------- 4 | 沈阳 | 2 7 | 和平区 | 4 8 | 沈河区 | 4 (3 rows)
使用CTE的好处
- 简化SQL代码的嵌套,提高代码可读性
- CTE的辅助语句只需要计算一次,在主查询中可以反复使用
- 当不需要共享查询结果的时候,相比比视图更轻量
PS:本文实例来自《PostgreSQL实战》
相关推荐
专注前端开发 2020-10-21
苏康申 2020-11-13
vitasfly 2020-11-12
oraclemch 2020-11-06
liuyang000 2020-09-25
FellowYourHeart 2020-10-05
赵继业 2020-08-17
whyname 2020-08-16
Seandba 2020-08-16
dbasunny 2020-08-16
拼命工作好好玩 2020-08-15
langyue 2020-08-15
写程序的赵童鞋 2020-08-03
Accpcjg 2020-08-02
tydldd 2020-07-30
好记忆也需烂 2020-07-28
jianghero 2020-07-28