日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

postgres之窗口函数

發布時間:2024/1/18 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 postgres之窗口函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本博客內容來自于官方文檔:http://www.postgres.cn/docs/10/functions-window.html#FUNCTIONS-WINDOW-TABLE

一、窗口函數介紹

一個窗口函數在一系列與當前行有某種關聯的表行上執行一種計算。這與一個聚集函數所完成的計算有可比之處。但是窗口函數并不會使多行被聚集成一個單獨的輸出行,這與通常的非窗口聚集函數不同。取而代之,行保留它們獨立的標識。在這些現象背后,窗口函數可以訪問的不僅僅是查詢結果的當前行。

下面是一個例子用于展示如何將每一個員工的薪水與他/她所在部門的平均薪水進行比較: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;

epname | 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被忽略,則默認幀包含整個分區中所有的行。?[4]?下面是使用sum的例子:SELECT salary, sum(salary) OVER () FROM empsalary;

salary | sum --------+-------5200 | 471005000 | 471003500 | 471004800 | 471003900 | 471004200 | 471004500 | 471004800 | 471006000 | 471005200 | 47100 (10 rows)

如上所示,由于在OVER子句中沒有ORDER BY,窗口幀和分區一樣,而如果缺少PARTITION BY則和整個表一樣。換句話說,每個合計都會在整個表上進行,這樣我們為每一個輸出行得到的都是相同的結果。但是如果我們加上一個ORDER BY子句,我們會得到非常不同的結果:SELECT salary, sum(salary) OVER (ORDER BY salary) FROM empsalary;

salary | sum --------+-------3500 | 35003900 | 74004200 | 116004500 | 161004800 | 257004800 | 257005000 | 307005200 | 411005200 | 411006000 | 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);

二、窗口函數調用

一個窗口函數調用表示在一個查詢選擇的行的某個部分上應用一個聚合類的函數。和非窗口聚合調用不同,這不會被約束為將被選擇的行分組為一個單一的輸出行 — 在查詢輸出中每一個行仍保持獨立。 但是,根據窗口函數調用的分組規范(PARTITION BY列表), 窗口函數可以訪問將成為當前行組的一部分的所有行。 一個窗口函數調用的語法是下列之一:

function_name ([expression [, expression ... ]]) [ FILTER ( WHERE filter_clause ) ] OVER window_name function_name ([expression [, expression ... ]]) [ FILTER ( WHERE filter_clause ) ] OVER ( window_definition ) function_name ( * ) [ FILTER ( WHERE filter_clause ) ] OVER window_name function_name ( * ) [ FILTER ( WHERE filter_clause ) ] OVER ( window_definition )

其中window_definition的語法是

[ existing_window_name ] [ PARTITION BY expression [, ...] ] [ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ] [ frame_clause ]

而可選的frame_clause是下列之一

{ RANGE | ROWS } frame_start { RANGE | ROWS } BETWEEN frame_start AND frame_end

其中frame_start和frame_end可以是下面形式中的一種

UNBOUNDED PRECEDING value PRECEDING CURRENT ROW value FOLLOWING UNBOUNDED FOLLOWING

?

這里,expression表示任何自身不含有窗口函數調用的值表達式。

window_name是對定義在查詢的WINDOW子句中的一個命名窗口聲明的引用。還可以使用在WINDOW子句中定義命名窗口的相同語法在圓括號內給定一個完整的window_definition,詳見SELECT參考頁。值得指出的是,OVER wname并不嚴格地等價于OVER (wname ...),后者表示復制并修改窗口定義,并且在被引用窗口聲明包括一個幀子句時會被拒絕。

PARTITION BY子句將查詢的行分組成為分區,窗口函數會獨立地處理它們。PARTITION BY工作起來類似于一個查詢級別的GROUP BY子句,不過它的表達式總是只是表達式并且不能是輸出列的名稱或編號。如果沒有PARTITION BY,該查詢產生的所有行被當作一個單一分區來處理。ORDER BY子句決定被窗口函數處理的一個分區中的行的順序。它工作起來類似于一個查詢級別的ORDER BY子句,但是同樣不能使用輸出列的名稱或編號。如果沒有ORDER BY,行將被以未指定的順序被處理。

frame_clause指定構成窗口幀的行集合,它是當前分區的一個子集,窗口函數將作用在該幀而不是整個分區。 幀可以被指定為RANGE或ROWS模式,在兩種情況中它都從frame_start運行到frame_end。如果frame_end被忽略,它默認運行到CURRENT ROW。

UNBOUNDED PRECEDING的一個frame_start表示該幀開始于分區的第一行,類似地UNBOUNDED FOLLOWING的一個frame_end表示該幀結束于分區的最后一行。

在RANGE模式下,?CURRENT ROW的一個frame_start表示該幀開始于當前行的第一個平級行(一個被ORDER BY認為與當前行等效的行),而CURRENT ROW的一個frame_end表示該幀結束于最后一個等效的ORDER BY平級行。在ROWS模式下,CURRENT ROW僅表示當前行。

value?PRECEDING和value?FOLLOWING情況當前只在ROWS模式中被允許。它們指示幀開始或結束于當前行之前或之后的指定數量的行。value必須是一個不包含任何變量、聚合函數或窗口函數的整數表達式。該值不能為空或負,但是可以為零,零表示只選擇當前行。

默認的幀選項是RANGE UNBOUNDED PRECEDING,它和RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW相同。如果使用ORDER BY,這會把該幀設置為從分區開始一直到當前行的最后一個ORDER BY平級行的所有行。如果不使用ORDER BY,分區中所有的行都被包括在窗口幀中,因為所有行都成為了當前行的平級行。

限制是frame_start不能為UNBOUNDED FOLLOWING、frame_end不能為UNBOUNDED PRECEDING并且在上述列表中frame_end的選擇不能早于frame_start的選擇出現 — 例如RANGE BETWEEN CURRENT ROW AND?valuePRECEDING是不被允許的。

如果指定了FILTER,那么只有對filter_clause計算為真的輸入行會被交給該窗口函數,其他行會被丟棄。只有是聚合的窗口函數才接受FILTER?。

內建的窗口函數在表?9.57中描述。 用戶可以增加其他的窗口函數。還有,任何內建或用戶定義的通用或統計聚合 函數可以被用作窗口函數。(有序聚合和假設集聚合目前不能用作窗口函數。)

使用*的語法被用來把參數較少的聚合函數當作窗口函數調用, 例如count(*) OVER (PARTITION BY x ORDER BY y)。 星號(*)通常通常不用于窗口特定的函數。窗口特定的函數不允許在函數參數列表中使用DISTINCT或ORDER BY。

只有在SELECT列表和查詢的ORDER BY子句中才允許窗口函數調用。

三、窗口函數處理

如果查詢包含任何窗口函數(見第?3.5?節、第?9.21?節和第?4.2.8?節),這些函數將在任何分組、聚集和HAVING過濾被執行之后被計算。也就是說如果查詢使用了任何聚集、GROUP BY或HAVING,則窗口函數看到的行是分組行而不是來自于FROM/WHERE的原始表行。

當多個窗口函數被使用,所有在窗口定義中有句法上等效的PARTITION BY和ORDER BY子句的窗口函數被保證在數據上的同一趟掃描中計算。因此它們將會看到相同的排序順序,即使ORDER BY沒有唯一地決定一個順序。但是,對于具有不同PARTITION BY或ORDER BY定義的函數的計算沒有這種保證(在這種情況中,在多個窗口函數計算之間通常要求一個排序步驟,并且并不保證保留行的順序,即使它的ORDER BY把這些行視為等效的)。

目前,窗口函數總是要求排序好的數據,并且這樣查詢的輸出總是被根據窗口函數的PARTITION BY/ORDER BY子句的一個或者另一個排序。但是,我們不推薦依賴于此。如果你希望確保結果以特定的方式排序,請顯式使用頂層的ORDER BY子句。

四、通用窗口函數

函數返回類型描述
row_number()bigint當前行在其分區中的行號,從1計
rank()bigint帶間隙的當前行排名; 與該行的第一個同等行的row_number相同
dense_rank()bigint不帶間隙的當前行排名; 這個函數計數同等組
percent_rank()double precision當前行的相對排名: (rank- 1) / (總分區行數 - 1)
cume_dist()double precision累積分配: (當前行前面的分區行數 或 與當前行同等的行的分區行數)/(總分區行數)
ntile(num_buckets?integer)integer從1到參數值的整數范圍,盡可能等分分區
lag(value?anyelement?[,?offsetinteger?[,?default?anyelement?]])和value的類型相同返回value, 它在分區內當前行的之前offset個位置的行上計算;如果沒有這樣的行,返回default替代。 (作為value必須是相同類型)。offset和default都是根據當前行計算的結果。如果忽略它們,則offset默認是1,default默認是空值
lead(value?anyelement?[,?offsetinteger?[,?default?anyelement?]])和value類型相同返回value,它在分區內當前行的之后offset個位置的行上計算;如果沒有這樣的行,返回default替代。(作為value必須是相同類型)。offset和default都是根據當前行計算的結果。如果忽略它們,則offset默認是1,default默認是空值
first_value(value?any)same type as?value返回在窗口幀中第一行上計算的value
last_value(value?any)和value類型相同返回在窗口幀中最后一行上計算的value
nth_value(value?any,?nth?integer)和value類型相同返回在窗口幀中第nth行(行從1計數)上計算的value;沒有這樣的行則返回空值

在表?9.57中列出的所有函數都依賴于相關窗口定義的ORDER BY子句指定的排序順序。當僅考慮ORDER BY列時,不能區分的行被稱為是同等行。定義的這四個排名函數(包括cume_dist) ,對于所有同等行的答案相同。

注意first_value、last_value和nth_value只考慮“窗口幀”內的行,它默認情況下包含從分區的開始行直到當前行的最后一個同等行。這對last_value可能不會給出有用的結果,有時對nth_value也一樣。你可以通過向OVER子句增加一個合適的幀聲明(RANGE或ROWS)來重定義幀。關于幀聲明的更多信息請參考第?4.2.8?節。

當一個聚集函數被用作窗口函數時,它將在當前行的窗口幀內的行上聚集。 一個使用ORDER BY和默認窗口幀定義的聚集產生一種“運行時求和”類型的行為,這可能是或者不是想要的結果。為了獲取在整個分區上的聚集,忽略ORDER BY或者使用ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING。 其它窗口幀聲明可以用來獲得其它的效果。

?

?

?

總結

以上是生活随笔為你收集整理的postgres之窗口函数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。