with&with recursive
部分参考自德哥的文章
https://github.com/digoal/blog
定义
with中SELECT的本意是为了将复杂的查询分解为更简单的部分
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_units,
SUM(amount) AS product_sales
FROM orders
WHERE region IN (SELECT region FROM top_regions)
GROUP BY region, product;
它显示了每个产品仅在销售区域的销售总额。with子句定义了两个名为 regional_sales 和 top_regions的辅助语句, regional_sales的输出用于top_regions,而top_regions的输出用于初级的SELECT查询。这个例子也可以不用WITH来写,但是需要两级嵌套的子SELECT查询。用这种方法更容易理解。
可选的RECURSIVE修饰符将WITH 从一个单纯的语法方便改变为在SQL标准中不可能实现的功能。使用RECURSIVE,一个WITH查询可以引用它自己的输出。一个非常简单的例子是查询1到100的和:
WITH RECURSIVE t(n) AS (
VALUES (1)
UNION ALL
SELECT n+1 FROM t WHERE n < 100
)
SELECT sum(n) FROM t;
一个递归WITH查询的一般形式总是一个non-recursive term,然后UNION(或者UNION ALL),然后一个recursive term,其中只有递归的术语可以包含一个对查询自己输出的引用。应用
从根到叶子
create table tbl_tree (cid integer,cname text,pid integer);
insert into tbl_tree values(1,'a',null),(2,'aa',1),(3,'ab',1),(4,'aaa',2),(5,'aba',3);
select * from tbl_tree;
with recursive t as (
select * from tbl_tree where cid=1
union all
select tbl_tree.cid, t.cname || '->' || tbl_tree.cname , tbl_tree.pid from tbl_tree join t on (t.cid=tbl_tree.pid)
)
select * from t;
1,'a',
2,'a->aa',1
3,'a->ab',1
4,'a->aa->aaa',2
5,'a->ab->aba',3
从叶子到根
with RECURSIVE t as (
select res_name::text,phy_res_code,parent_res_id from tbl_res where res_tree_level7_id='546423' and res_type_id=1001
union
select tbl_res.res_name || ' -> ' || t.res_name,t.phy_res_code,tbl_res.parent_res_id from t join tbl_res on (t.parent_res_id=tbl_res.res_id)
)
select * from t where parent_res_id=0;
使用with语句优化SQL性能
对于一张大表,某一个选择性很差的字段,需要求其count(distinct )的时候,使用with语句可以更快的得到结果。
例如:
select count(distinct sex) from table;
imos=# create table sex (sex char(1), otherinfo text);
CREATE TABLE
imos=# insert into sex select 'm', generate_series(1,10000000)||'this is test';
INSERT 0 10000000
imos=# insert into sex select 'w', generate_series(1,10000000)||'this is test';
INSERT 0 10000000
imos=# select count(distinct sex) from sex;
count
-------
2
(1 row)
Time: 10730.305 ms (00:10.730)
imos=# select sex from sex t group by sex;
sex
-----
m
w
(2 rows)
Time: 3408.709 ms (00:03.409)
创建索引再次查寻
imos=# create index idx_sex_1 on sex(sex);
CREATE INDEX
Time: 48104.539 ms (00:48.105)
imos=# set enable_seqscan=off;
SET
Time: 29.942 ms
imos=# explain select count(distinct sex) from sex;
QUERY PLAN
--------------------------------------------------------------------------------------------
Aggregate (cost=548681.74..548681.75 rows=1 width=8)
-> Index Only Scan using idx_sex_1 on sex (cost=0.44..498681.74 rows=20000000 width=2)
(2 rows)
Time: 38.064 ms
imos=# explain select sex from sex t group by sex;
QUERY PLAN
----------------------------------------------------------------------------------------------
Group (cost=0.44..548681.74 rows=2 width=2)
Group Key: sex
-> Index Only Scan using idx_sex_1 on sex t (cost=0.44..498681.74 rows=20000000 width=2)
(3 rows)
Time: 0.209 ms
imos=# select count(distinct sex) from sex;
count
-------
2
(1 row)
Time: 19646.478 ms (00:19.646)
imos=# select sex from sex t group by sex;
sex
-----
m
w
(2 rows)
Time: 2961.045 ms (00:02.961)
使用with结合btree索引查询
imos=# with recursive tmp_t as (
imos(# select min(sex) as sex from sex where sex is not null
imos(# union all
imos(# select (select min(t.sex) from sex t where t.sex > s.sex and t.sex is not null) from tmp_t s where s.sex is not null
imos(# )
imos-# select sex from tmp_t where sex is not null;
sex
-----
m
w
(2 rows)
Time: 0.490 ms
imos=# with recursive tmp_t as (
imos(# select min(sex) as sex from sex where sex is not null
imos(# union all
imos(# select (select min(t.sex) from sex t where t.sex > s.sex and t.sex is not null) from tmp_t s where s.sex is not null
imos(# )
imos-# select count(sex) from tmp_t where sex is not null;
count
-------
2
(1 row)
Time: 0.369 ms
imos=#
with语句解海盗分金问题
https://blog.csdn.net/enmotech/article/details/80237144
扫描二维码关注公众号,回复:
4967830 查看本文章