with&with recursive

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 查看本文章

猜你喜欢

转载自blog.csdn.net/weixin_42767321/article/details/86507043