一個視窗函式會對一組與當前行相關的錶行執行計算。這類似於可以使用聚合函式完成的計算型別。然而,視窗函式不會像非視窗聚合呼叫那樣導致行被分組為單個輸出行。相反,行保留其獨立的身份。在後臺,視窗函式能夠訪問查詢結果中除當前行以外的內容。
下面是一個示例,展示瞭如何將每個員工的薪資與其部門的平均薪資進行比較
SELECT depname, empno, salary, avg(salary) OVER (PARTITION BY depname) FROM empsalary;
depname | empno | salary | avg -----------+-------+--------+----------------------- develop | 11 | 5200 | 5020.0000000000000000 develop | 7 | 4200 | 5020.0000000000000000 develop | 9 | 4500 | 5020.0000000000000000 develop | 8 | 6000 | 5020.0000000000000000 develop | 10 | 5200 | 5020.0000000000000000 personnel | 5 | 3500 | 3700.0000000000000000 personnel | 2 | 3900 | 3700.0000000000000000 sales | 3 | 4800 | 4866.6666666666666667 sales | 1 | 5000 | 4866.6666666666666667 sales | 4 | 4800 | 4866.6666666666666667 (10 rows)
前三列輸出直接來自表empsalary
,並且對於表中的每一行都有一個輸出行。第四列表示對錶中具有與當前行相同depname
值的行進行的平均值。(這實際上與非視窗avg
聚合函式是同一個函式,但OVER
子句使其被視為視窗函式並根據視窗幀進行計算。)
視窗函式呼叫總是包含一個緊跟在視窗函式名稱和引數後面的OVER
子句。這在語法上將其與普通函式或非視窗聚合區分開來。OVER
子句精確地確定查詢的行如何被分割以供視窗函式處理。OVER
中的PARTITION BY
子句將行劃分為組或分割槽,這些分割槽共享PARTITION BY
表示式的相同值。對於每一行,視窗函式都在與當前行屬於同一分割槽的行上進行計算。
您還可以使用OVER
中的ORDER BY
來控制視窗函式處理行的順序。(視窗ORDER BY
甚至不需要匹配行的輸出順序。)下面是一個示例
SELECT depname, empno, salary, row_number() OVER (PARTITION BY depname ORDER BY salary DESC) FROM empsalary;
depname | empno | salary | row_number -----------+-------+--------+------------ develop | 8 | 6000 | 1 develop | 10 | 5200 | 2 develop | 11 | 5200 | 3 develop | 9 | 4500 | 4 develop | 7 | 4200 | 5 personnel | 2 | 3900 | 1 personnel | 5 | 3500 | 2 sales | 1 | 5000 | 1 sales | 4 | 4800 | 2 sales | 3 | 4800 | 3 (10 rows)
如圖所示,row_number
視窗函式根據ORDER BY
子句定義的順序為每個分割槽內的行分配順序編號(具有相同值的行按未指定順序編號)。row_number
不需要顯式引數,因為其行為完全由OVER
子句決定。
視窗函式考慮的行是查詢的FROM
子句生成的“虛擬表”中的行,並由其WHERE
、GROUP BY
和HAVING
子句(如果存在)進行過濾。例如,因不符合WHERE
條件而被刪除的行不會被任何視窗函式看到。一個查詢可以包含多個視窗函式,它們使用不同的OVER
子句以不同的方式分割資料,但它們都作用於此虛擬表定義的同一組行。
我們已經看到,如果行的順序不重要,則可以省略ORDER BY
。也可以省略PARTITION BY
,在這種情況下,有一個單一分割槽包含所有行。
視窗函式還有另一個重要概念:對於每一行,其分割槽中有一個稱為其視窗幀的行集。一些視窗函式僅作用於視窗幀中的行,而不是整個分割槽。預設情況下,如果提供了ORDER BY
,則幀由分割槽開頭到當前行的所有行組成,再加上任何與當前行根據ORDER BY
子句相等的後續行。ORDER BY
被省略時,預設幀由分割槽中的所有行組成。[5]下面是一個使用sum
的示例
SELECT salary, sum(salary) OVER () FROM empsalary;
salary | sum --------+------- 5200 | 47100 5000 | 47100 3500 | 47100 4800 | 47100 3900 | 47100 4200 | 47100 4500 | 47100 4800 | 47100 6000 | 47100 5200 | 47100 (10 rows)
上面,由於OVER
子句中沒有ORDER BY
,視窗幀與分割槽相同,而由於缺少PARTITION BY
,整個表就相當於一個分割槽;換句話說,每個總和都是對整個表計算的,因此我們為每一行輸出得到相同的結果。但是如果我們新增一個ORDER BY
子句,我們將得到非常不同的結果
SELECT salary, sum(salary) OVER (ORDER BY salary) FROM empsalary;
salary | sum --------+------- 3500 | 3500 3900 | 7400 4200 | 11600 4500 | 16100 4800 | 25700 4800 | 25700 5000 | 30700 5200 | 41100 5200 | 41100 6000 | 47100 (10 rows)
這裡的總和是從最低薪資到當前薪資計算的,包括當前薪資的任何重複項(注意重複薪資的結果)。
視窗函式僅允許出現在查詢的SELECT
列表和ORDER BY
子句中。它們在其他地方被禁止,例如在GROUP BY
、HAVING
和WHERE
子句中。這是因為它們在邏輯上是在處理完這些子句之後執行的。此外,視窗函式在非視窗聚合函式之後執行。這意味著可以在視窗函式的引數中包含聚合函式呼叫,但反之則不行。
如果需要在視窗計算執行後過濾或分組行,可以使用子查詢。例如
SELECT depname, empno, salary, enroll_date FROM (SELECT depname, empno, salary, enroll_date, row_number() OVER (PARTITION BY depname ORDER BY salary DESC, empno) AS pos FROM empsalary ) AS ss WHERE pos < 3;
上面的查詢僅顯示內部查詢中row_number
小於3的行(即每個部門的前兩行)。
當查詢涉及多個視窗函式時,可以為每個函式寫出單獨的OVER
子句,但這會重複且容易出錯,如果多個函式需要相同的視窗行為。相反,每個視窗行為可以在WINDOW
子句中命名,然後在OVER
中引用。例如
SELECT sum(salary) OVER w, avg(salary) OVER w FROM empsalary WINDOW w AS (PARTITION BY depname ORDER BY salary DESC);
如果您在文件中看到任何不正確的內容、與您對特定功能的經驗不符或需要進一步澄清的內容,請使用此表格報告文件問題。