本文是轉載,原文地址是: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不支持