PostgreSQL 高級SQL(二) filter子句


本文是轉載,原文地址是:https://www.jianshu.com/p/aad5b7265674

 

本章所用到案例數據來自於上一章節,如果有想使用該數據的讀者可以查看上一章節。

這一章節我們想要了解的是PG聚合操作中使用到的filter子句,這個filter子句是ANSI SQL標准中的關鍵字,並不是PG的專用SQL關鍵字。如果我們想了解中國、美國、日本、法國、德國、加拿大從1960~2018年中每隔十年的GDP總值情況,我們可能會寫出着這樣的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;

從上圖可以看出美國的經濟體量和我們中國的經濟體量不是在一個數量級的,中國每隔十年的GDP實現一倍的增速,美國一直飛速發展時期,中國要實現美國的GDP的話粗略估計需要至少30年的時間甚至更久;

回歸正題,我們今天的主角是filter子句,ANSI SQL加入filter關鍵詞的主要目的就是替代case when子句,簡化case when參與的聚合語句,增加可可讀性,我們用同樣的filter 子句實現上面的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;

從上面的結果我們可以看出filter子句和case when 子句在聚合函數中使用是等價的,並且filter子句的可讀性更好,讓人一眼就能看出SQL的目的和作用,

下面我們看一下上面倆個語句的的執行計划:

從上面的結果我們可以看出來倆種語句不僅結果一樣而且產生的執行計划也是一致的,並且倆個語句值進行了一次全表掃描就計算出了結果,在平時的開發中,很多開發者為了實現相同的結果可能要進行五次全表掃描,很可能會寫出以下的相同查詢結果但是不同性能的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;

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

從上面的查詢計划我們可以看出來經過了五次全表掃描,五次聚合,如果這個表的數據量很大,那么性能可想而知。

最后我想說的是filter適合所有的聚合函數,不僅僅是PG內置的的聚合函數,還支持安裝擴展包的聚合函數,總之filter子句非常的棒!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM