PostgreSQL 高級SQL(四) 滑動窗口函數


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

 

上章節我們講述的窗口函數都屬於靜態窗口,然而我們很多場景是需要滑動窗口,比如我們需要查看這樣的一張報表,這張報表包含國家名字,年份,GDP,當前年份與上一年、下一年的GDP均值,也就是說GDP均值這一列隨着行數的推移,動態移動變化的,那么我們可以借助PG的滑動窗口來完成這個功能,SQL如下

SELECT
	country_name,
	"year",
	gdp,
	AVG ( gdp ) OVER ( PARTITION BY country_name ORDER BY "year" DESC ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING ) 
FROM
	country_gdp_year_final ff  
WHERE
	country_code IN ( 'CHN', 'JPN', 'USA', 'DEU', 'CAN', 'FRA' ) 
	AND "year" BETWEEN 2012 AND 2017;

(獲取每年與前后倆年的均值GDP)

preceding 中文意思:前面的

following 中文意思 :后面的

上面的 rows between 1 preceding and 1 following 定義的滑動窗口包含三行,當前行,當前行的前一行,當前行的后一行

我們可以計算一下每行的平均值的意義,首先我們的窗口限制在國家這個字段窗口里面,並且按照年份降序排序,

中國2017年avg(gdp)=(2017GDP+2016GDP)/2 因為沒有選擇2018年所以2017年的前一年是不存在的,后一年是2016年

中國2016年avg(gdp)=(2017GDP+2016GDP+2015GDP)/3 2016年是當前年份

中國2015年avg(gdp)=(2016GDP+2015GDP+2014GDP)/3 2015年是當前年份

......

中國2012年avg(gdp)=(2013GDP+2012GDP)/2 2012年是當前年份,因為沒有選擇2011年,所以2012年的后一年不存在,前一年是2013

我們可以通過列轉行函數array_agg來進行一下更加直觀的認識

SELECT
	country_name,
	"year",
	gdp,
	ARRAY_AGG ( gdp ) OVER ( PARTITION BY country_name ORDER BY "year" DESC ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING ) 
FROM
	country_gdp_year_final ff 
WHERE
	country_code IN ( 'CHN', 'JPN', 'USA', 'DEU', 'CAN', 'FRA' ) 
	AND "year" BETWEEN 2012 AND 2017;

 

PG里面的滑動窗口還有一個關鍵詞:unbounded(無屆的),這個關鍵詞可以放在preceding ,following之前

SELECT
	country_name,
	"year",
	gdp,
	ARRAY_AGG ( "year" ) OVER ( PARTITION BY country_name ORDER BY "year" DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING ) 
FROM
	country_gdp_year_final 
WHERE
	country_code IN ( 'CHN', 'JPN', 'USA', 'DEU', 'CAN', 'FRA' ) 
	AND "year" BETWEEN 2012 AND 2017;

  

總結:從上面的示例中我們可以看出滑動窗口函數的強大,但是需要記住的一點是:移動窗口需要配合order by子句一起使用,如果沒有order by的話,就會出現問題,因為數據沒有事先排序,那么滑動窗口計算出來的數據就會出出現隨機,我們可以將order by 子句去掉來看一下結果

SELECT
	country_name,
	"year",
	gdp,
	ARRAY_AGG ( "year" ) OVER ( PARTITION BY country_name ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING ) 
FROM
	country_gdp_year_final 
WHERE
	country_code IN ( 'CHN', 'JPN', 'USA', 'DEU', 'CAN', 'FRA' ) 
	AND "year" BETWEEN 2012 AND 2017;

如果我們想要獲取的不僅僅是平均值還有最大值和最小值這倆列,我們可以這樣使用

SELECT
	country_name,
	"year",
	gdp,
	AVG ( gdp ) OVER ( PARTITION BY country_name ORDER BY "year" DESC ),
	MIN ( gdp ) OVER ( PARTITION BY country_name ORDER BY "year" DESC ),
	MAX ( gdp ) OVER ( PARTITION BY country_name ORDER BY "year" DESC ) 
FROM
	country_gdp_year_final 
WHERE
	country_code IN ( 'CHN', 'JPN', 'USA', 'DEU', 'CAN', 'FRA' ) 
	AND "year" BETWEEN 2012 AND 2017;

我們看一下上面的SQL代碼,可以看到 over()括號里的代碼都是一樣的,在此,PG為我們提供一個提取窗口子句的功能,我們可以將上面的代碼轉換為下面等價可讀性更好的代碼

SELECT
	country_name,
	"year",
	gdp,
	AVG ( gdp ) OVER ( myWindows ),
	MIN ( gdp ) OVER ( myWindows ),
	MAX ( gdp ) OVER ( myWindows ) 
FROM
	country_gdp_year_final  
WHERE
	country_code IN ( 'CHN', 'JPN', 'USA', 'DEU', 'CAN', 'FRA' ) 
	AND "year" BETWEEN 2012 AND 2017 WINDOW myWindows AS ( PARTITION BY country_name ORDER BY "year" DESC );

可以看到使用window提取子句后的代碼可讀性更好,但是使用window的時候,上面的滑動窗口是不可以使用window提取子句的,目前PG不支持


免責聲明!

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



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