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)

再舉一個稍微複雜點的例子,

  1. 先創建一張表
mydb@localhost:5432=>create table test_area(id int4, name varchar(32), fatherid int4);
CREATE TABLE
  1. 向表裏面寫入測試數據
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)

上面的數據明顯具有層級關係

  1. 現在我們的需求是這個樣子的,給定一個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)
  1. 入股我們修改下需求,例如給出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實戰》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章