視窗函數會在一組與目前列相關的表格列上執行計算。 這種類似於使用彙總函數可以完成的計算類型。 但是,視窗函數不會像非視窗彙總呼叫那樣導致列分組為單一輸出列。 相反地,這些列會保留其各自的識別身分。 在幕後,視窗函數能夠存取的不僅僅是查詢結果的目前列。
以下範例顯示如何將每位員工的薪水與其部門的平均薪水進行比較
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, rank() OVER (PARTITION BY depname ORDER BY salary DESC) FROM empsalary;
depname | empno | salary | rank -----------+-------+--------+------ develop | 8 | 6000 | 1 develop | 10 | 5200 | 2 develop | 11 | 5200 | 2 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 | 2 (10 rows)
如此處所示,rank
函數使用 ORDER BY
子句定義的順序,為目前列分割區中每個不同的 ORDER BY
值產生一個數值排名。 rank
不需要明確的參數,因為其行為完全由 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, rank() OVER (PARTITION BY depname ORDER BY salary DESC, empno) AS pos FROM empsalary ) AS ss WHERE pos < 3;
上述查詢僅顯示內部查詢中 rank
小於 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);
有關視窗函數的更多詳細資訊,請參閱 第 4.2.8 節、第 9.22 節、第 7.2.5 節 和 SELECT 參考頁面。
如果您在文件中發現任何不正確、與您特定功能的使用經驗不符或需要進一步澄清的地方,請使用 此表格 回報文件問題。