Oracl Over函數
Oracl Over函數
簡介
在介紹Over之前, 必須提到開窗函數, 與 聚 合函數一樣, 開窗函數也是對行集組進行聚合計算, 但是它不像普通聚合函數那樣每組只返回一個值, 開窗函數可以為每組返回多個值.
開窗函數用於為行定義一個窗口(這裏的窗口是指運算將要操作的行的集合),它對一組值進行操作,不需要使用GROUP BY子句對數據進行分組,
能夠在同一行中同時返回基礎行的列和聚合列。
在 Oracle中則被稱為分析函數,而在DB2中則被稱為OLAP函數。
Over 關鍵字表示把函數當成開窗函數而不是聚合函數。SQL標準允許將所有聚合函數用做開窗函數,使用OVER 關鍵字來區分這兩種用法。
開窗函數的調用格式為:
函數名(列) OVER(選項)
案例及解釋
創建一張簡單的表, 同時在表內插入15條數據:
create table classInfo( studentName varchar2(20), classNumber number(2), score number(2) ); INSERT INTO CLASSINFO (STUDENTNAME, CLASSNUMBER, SCORE) VALUES (‘1‘, ‘1‘, ‘11‘); INSERT INTO CLASSINFO (STUDENTNAME, CLASSNUMBER, SCORE) VALUES (‘2‘, ‘1‘, ‘22‘); INSERT INTO CLASSINFO (STUDENTNAME, CLASSNUMBER, SCORE) VALUES (‘3‘, ‘1‘, ‘33‘); INSERT INTO CLASSINFO (STUDENTNAME, CLASSNUMBER, SCORE) VALUES (‘4‘, ‘1‘, ‘44‘); INSERT INTO CLASSINFO (STUDENTNAME, CLASSNUMBER, SCORE) VALUES (‘5‘, ‘1‘, ‘55‘); INSERT INTO CLASSINFO (STUDENTNAME, CLASSNUMBER, SCORE) VALUES (‘6‘, ‘2‘, ‘1‘); INSERT INTO CLASSINFO (STUDENTNAME, CLASSNUMBER, SCORE) VALUES (‘7‘, ‘2‘, ‘2‘); INSERT INTO CLASSINFO (STUDENTNAME, CLASSNUMBER, SCORE) VALUES (‘8‘, ‘2‘, ‘3‘); INSERT INTO CLASSINFO (STUDENTNAME, CLASSNUMBER, SCORE) VALUES (‘9‘, ‘2‘, ‘4‘); INSERT INTO CLASSINFO (STUDENTNAME, CLASSNUMBER, SCORE) VALUES (‘10‘, ‘2‘, ‘5‘); INSERT INTO CLASSINFO (STUDENTNAME, CLASSNUMBER, SCORE) VALUES (‘11‘, ‘3‘, ‘10‘); INSERT INTO CLASSINFO (STUDENTNAME, CLASSNUMBER, SCORE) VALUES (‘12‘, ‘3‘, ‘20‘); INSERT INTO CLASSINFO (STUDENTNAME, CLASSNUMBER, SCORE) VALUES (‘13‘, ‘3‘, ‘30‘); INSERT INTO CLASSINFO (STUDENTNAME, CLASSNUMBER, SCORE) VALUES (‘14‘, ‘3‘, ‘40‘); INSERT INTO CLASSINFO (STUDENTNAME, CLASSNUMBER, SCORE) VALUES (‘15‘, ‘3‘, ‘50‘);
從這裏分別觀察其特性:
常規查詢, 不同班級分數的總和
SELECT CLASSNUMBER, SUM(SCORE) FROM CLASSINFO GROUP BY CLASSNUMBER; --結果如下 1 165 2 15 3 150
這是我們常常會用到的分組函數, 不同班級的總分.
但如果我們還想看到每個人的 name 和 分數呢?不難想出如下代碼:
SELECT STUDENTNAME, SCORE, A.CLASSNUMBER CLASSNUMBER, TOTALSCORE FROM CLASSINFO A LEFT OUTER JOIN (SELECT CLASSNUMBER, SUM(SCORE) TOTALSCORE FROM CLASSINFO GROUP BY CLASSNUMBER) B ON A.CLASSNUMBER = B.CLASSNUMBER ORDER BY CLASSNUMBER; --結果如下 1 11 1 165 2 22 1 165 3 33 1 165 4 44 1 165 5 55 1 165 6 1 2 15 7 2 2 15 8 3 2 15 9 4 2 15 10 5 2 15 11 10 3 150 12 20 3 150 13 30 3 150 14 40 3 150
使用Over之後:
SELECT STUDENTNAME, SCORE, CLASSNUMBER, SUM(SCORE) OVER(PARTITION BY CLASSNUMBER ) TOTALSCORE FROM CLASSINFO ORDER BY CLASSNUMBER; --結果與上述相同, 就不再展示
通過 PARTITION BY CLASSNUMBER 進行分組
讓我們嘗試下面幾種用法, 就不在這張表上做操作:
create table overDemo( dateNumber number(2), sale number(5) ); INSERT INTO OVERDEMO (DATENUMBER, SALE) VALUES (‘1‘, ‘10‘); INSERT INTO OVERDEMO (DATENUMBER, SALE) VALUES (‘2‘, ‘10‘); INSERT INTO OVERDEMO (DATENUMBER, SALE) VALUES (‘3‘, ‘20‘); INSERT INTO OVERDEMO (DATENUMBER, SALE) VALUES (‘4‘, ‘20‘); INSERT INTO OVERDEMO (DATENUMBER, SALE) VALUES (‘5‘, ‘30‘); INSERT INTO OVERDEMO (DATENUMBER, SALE) VALUES (‘6‘, ‘30‘); INSERT INTO OVERDEMO (DATENUMBER, SALE) VALUES (‘7‘, ‘40‘); INSERT INTO OVERDEMO (DATENUMBER, SALE) VALUES (‘8‘, ‘40‘); INSERT INTO OVERDEMO (DATENUMBER, SALE) VALUES (‘9‘, ‘50‘); INSERT INTO OVERDEMO (DATENUMBER, SALE) VALUES (‘10‘, ‘50‘); SELECT * FROM OVER DEMO; --表中數據如下 DATENUMBER SALE 1 10 2 10 3 20 4 20 5 30 6 30 7 40 8 40 9 50 10 50 SELECT DATENUMBER, SUM(SALE) OVER (ORDER BY DATENUMBER ) TATLESALE FROM OVERDEMO ORDER BY DATENUMBER; --結果如下 1 10 -- sale(1) 2 20 -- sale(1) + sale(2) 3 40 -- sale(1) + sale(2) + sale(3) 4 60 --... 5 90 --... 6 120 --... 7 160 --... 8 200 --... 9 250 --... 10 300 --sale(1) + sale(2) + ... + sale(10)
不難發現, 這種累加方式, 求和方式是 根據DATENUMBER 連續求和
SELECT DATENUMBER, SUM(SALE) OVER (ORDER BY DATENUMBER DESC ) TATLESALE FROM OVERDEMO ORDER BY DATENUMBER; --結果如下 1 300 2 290 3 280 4 260 5 240 6 210 7 180 8 140 9 100 10 50 SELECT DATENUMBER, SUM(SALE) OVER () TATLESALE FROM OVERDEMO ORDER BY DATENUMBER; --結果如下 1 300 2 300 3 300 4 300 5 300 6 300 7 300 8 300 9 300 SELECT DATENUMBER, SUM(SALE) OVER (ORDER BY SALE) TATLESALE FROM OVERDEMO ORDER BY DATENUMBER; --結果如下 1 20 2 20 3 60 4 60 5 120 6 120 7 200 8 200 9 300 10 300
小結
SUM() Over(ORDER BY COLUMN_NAME) 連續累加
SUM() Over(ORDER BY COLUMN_NAME DESC) 倒序累加
SUM() Over() 全部求和
註意
但需要註意的一個地方是: 在這裏使用 Order By,計算方式是: 會根據後面的字段, 首先進行分組, 然後再度進行累加:
如: ORDER BY SALE, 則類似於 SUM(SALE) FROM TABLE GROUP BY SALE, 對求得的結果集再度進行累加. 而在 DATENUMBER 累加的時候沒有出現這種現象, 是因為 DATENUMBER都是唯一值.
甚至 OVER(ORDER BY COLUMN_A, COLUMN_B), 也是先根據
SUM(COLUMN_AIM) FROM TABLE GROUP BY COLUMN_A, COLUMN_B, 對求得的結果集進行累加.PARTITION BY
SELECT STUDENTNAME, SCORE, CLASSNUMBER, SUM(SCORE) OVER(PARTITION BY CLASSNUMBER ORDER BY STUDENTNAME) TOTALSCORE FROM CLASSINFO ORDER BY CLASSNUMBER; --結果 1 11 1 11 2 22 1 33 3 33 1 66 4 44 1 110 5 55 1 165 10 5 2 5 6 1 2 6 7 2 2 8 8 3 2 11 9 4 2 15 11 10 3 10 12 20 3 30 13 30 3 60 14 40 3 100 15 50 3 150
而後再來看 PARTITION BY COLUMN_A ORDER BY COLUMN_B, 則是先通過 COLUMB_A 進行分組, 而後 在 分組的結果集內, 根據 COLUMN_B 再度進行累加操作.
總結
在 Over() 中, 會根據 Over前的聚合函數決定所需要的操作, 同時根據 Partition By 的列進行分組, 對於 分組後的數據, 再度通過 Order By 後的字段 進行相應的 累加 等操作.
在上面的案例中說來:
首先根據 CLASSNUMBER 班級編號, 對所有數據分組, 分成三組數據:
--1 S C SCORE 1 1 11 2 1 22 3 1 33 4 1 44 5 1 55 --2 6 2 1 7 2 2 8 2 3 9 2 4 10 2 5 --3 11 3 10 12 3 20 13 3 30 14 3 40 15 3 50
而後分別根據三組數據中的 STUDENTNAME 進行分組, 分組結果與上面相同,不同的是在 當前的每一組內 又分成 5 組數據, 而後在 每一組內, 根據新分出來的 5 組數據進行 累加 求和.
如果將 Order By StudentName 改為 ClassNumber, 可以猜想結果:
按照剛才的理論來說: 仍然是分成上面三組數據, 在第二次分組中又有所區別, 三組中每一組內的 ClassNumber都相同, 因此 歸結為一組數據.在組內累加求和 也僅僅進行一次 累加結果分別為 165 15 150. 因此最終的結果不難猜想:
1 11 1 165 2 22 1 165 3 33 1 165 4 44 1 165 5 55 1 165 10 5 2 15 6 1 2 15 7 2 2 15 8 3 2 15 9 4 2 15 11 10 3 150 12 20 3 150 13 30 3 150 14 40 3 150 15 50 3 150
自己測試了一遍, 結果一致.
註意
以上理論僅僅是根據結果自己猜想, 方便理解. 並沒有官方文檔的 說明來支撐.
Rank( )
需要再提到的一點是:
比較常用到的另一個地方是 Rank() Over();
它的功能也比較簡單, 排序, 做名次的時候會用到.
--在此之前先對之前的 CLASSINFO 表數據略做修改
UPDATE CLASSINFO SET SCORE = ‘11‘ WHERE STUDENTNAME = ‘6‘;
UPDATE CLASSINFO SET SCORE = ‘22‘ WHERE STUDENTNAME = ‘7‘;
UPDATE CLASSINFO SET SCORE = ‘33‘ WHERE STUDENTNAME = ‘8‘;
SELECT STUDENTNAME, SCORE, CLASSNUMBER,
RANK() OVER(ORDER BY SCORE) AS 名次 FROM CLASSINFO;
--結果
9 4 2 1
10 5 2 2
11 10 3 3
1 11 1 4
6 11 2 4
12 20 3 6
7 22 2 7
2 22 1 7
13 30 3 9
3 33 1 10
8 33 2 10
14 40 3 12
4 44 1 13
15 50 3 14
5 55 1 15
名次的結果還是比較的清晰明了, 但在這裏需要註意的一個地方是, 在查詢的時候, 出現了兩個 第 4 名, 隨後接下來便是 第6名, 是的, 你沒想錯, 在相同的時候, 會將名次並列, 然後跳過下一個名次.
但又會出現一種比較常見的情況:
考試的時候, 總分相同者, 以數學論名次, 再相同, 語文...這樣的一套規則
SELECT STUDENTNAME, SCORE, CLASSNUMBER,
RANK() OVER(ORDER BY SCORE, STUDENTNAME) AS 名次 FROM CLASSINFO;
--結果
9 4 2 1
10 5 2 2
11 10 3 3
1 11 1 4
6 11 2 5
12 20 3 6
2 22 1 7
7 22 2 8
13 30 3 9
3 33 1 10
8 33 2 11
14 40 3 12
4 44 1 13
15 50 3 14
5 55 1 15
如上, 在order之後, 當相同時以所給的 STUDENTNAME進行排序, 雖然你想說規則不合理, 但 我是規則的制定者.
又有老師提出要求拉, 我僅僅向看我們班的名次該怎麽辦呢?
SELECT STUDENTNAME, SCORE, CLASSNUMBER,
RANK() OVER(PARTITION BY CLASSNUMBER ORDER BY SCORE) AS 名次 FROM CLASSINFO;
--結果
1 11 1 1
2 22 1 2
3 33 1 3
4 44 1 4
5 55 1 5
9 4 2 1
10 5 2 2
6 11 2 3
7 22 2 4
8 33 2 5
11 10 3 1
12 20 3 2
13 30 3 3
14 40 3 4
15 50 3 5
相信看過我對 Over的解釋, 理解這段也不算難. 就不再贅述.
DENSE_RANK( )
需求是無盡的, 如果主任又說, 我們學校排名, 並列第二名, 下一個孩子就該是第三名, 雖然只有四個人, 但好歹讓 家長開心下.
SELECT STUDENTNAME, SCORE, CLASSNUMBER,
DENSE_RANK() OVER(ORDER BY SCORE) AS 名次 FROM CLASSINFO;
--結果
9 4 2 1
10 5 2 2
11 10 3 3
1 11 1 4
6 11 2 4
12 20 3 5
7 22 2 6
2 22 1 6
13 30 3 7
3 33 1 8
8 33 2 8
14 40 3 9
4 44 1 10
15 50 3 11
5 55 1 12
這樣查詢下來, 一共15 個人, 我們家孩子 考了個 12 名, 還算不錯, 不是倒一. 家長得多開心呀.
在 DENSE_RANK() 中, 在 Rank() 中提到的規則 同樣適用.
到這裏, Over() 的講述也差不多告一段落.
Oracl Over函數