????本章所用到案例數(shù)據(jù)來自于上一章節(jié),如果有想使用該數(shù)據(jù)的讀者可以查看上一章節(jié)。
? ? 這一章節(jié)我們想要了解的是PG聚合操作中使用到的filter子句,這個(gè)filter子句是ANSI SQL標(biāo)準(zhǔn)中的關(guān)鍵字,并不是PG的專用SQL關(guān)鍵字。如果我們想了解中國(guó)、美國(guó)、日本、法國(guó)、德國(guó)、加拿大從1960~2018年中每隔十年的GDP平均值情況,我們可能會(huì)寫出著這樣的SQL,
select country_name,sum(case when year>=1960 and year<1970 then gdp else null end) as "1960~1969", sum(case when year>=1970 and year<1980 then gdp else null end) as "1970~1979", sum(case when year>=1980 and year<1990 then gdp else null end) as "1980~1989", sum(case when year>=1990 and year<2000 then gdp else null end) as "1990~1999", sum(case when year>=2000 then gdp else null end) as "2000~至今" from country_gdp_year_final where country_code in('CHN','JPN','USA','DEU','CAN','FRA') group by country_name;

從上圖可以看出美國(guó)的經(jīng)濟(jì)體量和我們中國(guó)的經(jīng)濟(jì)體量不是在一個(gè)數(shù)量級(jí)的,中國(guó)每隔十年的GDP實(shí)現(xiàn)一倍的增速,美國(guó)一直飛速發(fā)展時(shí)期,中國(guó)要實(shí)現(xiàn)美國(guó)的GDP的話粗略估計(jì)需要至少30年的時(shí)間甚至更久;
回歸正題,我們今天的主角是filter子句,ANSI SQL加入filter關(guān)鍵詞的主要目的就是替代case when子句,簡(jiǎn)化case when參與的聚合語句,增加可可讀性,我們用同樣的filter 子句實(shí)現(xiàn)上面的case when參與的聚合操作。
select country_name,sum(gdp) filter(where? year>=1960 and year<1970) as "1960~1969",sum(gdp)? filter(where? year>=1970 and year<1980) as "1970~1979",sum(gdp)? filter(where year>=1980 and year<1990)? as "1980~1989",sum(gdp)? filter(where? year>=1990 and year<2000) as "1990~1999", sum(gdp)? filter(where? year>=2000 ) as "2000~至今" from country_gdp_year_final? where? country_code in('CHN','JPN','USA','DEU','CAN','FRA') group by country_name;

從上面的結(jié)果我們可以看出filter子句和case when 子句在聚合函數(shù)中使用是等價(jià)的,并且filter子句的可讀性更好,讓人一眼就能看出SQL的目的和作用,
下面我們看一下上面?zhèn)z個(gè)語句的的執(zhí)行計(jì)劃:

????????從上面的結(jié)果我們可以看出來倆種語句不僅結(jié)果一樣而且產(chǎn)生的執(zhí)行計(jì)劃也是一致的,并且倆個(gè)語句值進(jìn)行了一次權(quán)標(biāo)掃描就計(jì)算出了結(jié)果,在平時(shí)的開發(fā)中,很多開發(fā)者為了實(shí)現(xiàn)相同的結(jié)果可能要進(jìn)行五次權(quán)標(biāo)掃描,很可能會(huì)寫出以下的相同查詢結(jié)果但是不同性能的SQL?
select ff.country_name,ff."1960~1969",aa."1970~1979",bb."1980~1989",cc."1990~1999",dd."2000~至今" from (
select country_name,sum(gdp) as "1960~1969"? from country_gdp_year_final ff where? year>=1960 and year<1970 and? country_code in('CHN','JPN','USA','DEU','CAN','FRA')? group by country_name)as ff?
left join(select country_name,sum(gdp) as "1970~1979"? from country_gdp_year_final where? year>=1970 and year<1980 and? country_code in('CHN','JPN','USA','DEU','CAN','FRA')? group by country_name)as aa? on aa.country_name=ff.country_name
left join(select country_name,sum(gdp) as "1980~1989"? from country_gdp_year_final where? year>=1980 and year<1990 and? country_code in('CHN','JPN','USA','DEU','CAN','FRA') group by country_name)as bb? on bb.country_name=ff.country_name?
left join(select country_name,sum(gdp) as "1990~1999"? from country_gdp_year_final where? year>=1990 and year<2000and? country_code in('CHN','JPN','USA','DEU','CAN','FRA')? group by country_name)as cc? on cc.country_name=ff.country_name?
left join(select country_name,sum(gdp) as "2000~至今"? from country_gdp_year_final where year>=2000 and? country_code in('CHN','JPN','USA','DEU','CAN','FRA')? group by country_name)as dd? on dd.country_name=ff.country_name;

我也相信很多人開發(fā)者寫出來的SQL和上面的SQL基本差不多,這種SQL不僅很長(zhǎng)而且很難都,更致命的是這種SQL進(jìn)行了五次全表掃描,在不考慮緩存命中的的情況下,這種SQL的查詢時(shí)間是上面filter和case when子句的五倍,我們可以看一下這個(gè)長(zhǎng)SQL的查詢計(jì)劃。

????????從上面的查詢計(jì)劃我們可以看出來經(jīng)過了五次全表掃描,五次聚合,如果這個(gè)表的數(shù)據(jù)量很大,那么性能可想而知。
? ? ? ? 最后我想說的是filter適合所有的聚合函數(shù),不僅僅是PG內(nèi)置的的聚合函數(shù),還支持安裝擴(kuò)展包的聚合函數(shù),總之filter子句非常的棒?。。?/p>