1. 程式人生 > >Oracl Over函數

Oracl Over函數

但是 需求 pda desc cor des 便是 格式 通過

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‘);

從這裏分別觀察其特性:

  1. 常規查詢, 不同班級分數的總和

    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
  2. 使用Over之後:

    SELECT STUDENTNAME, SCORE, CLASSNUMBER, SUM(SCORE) OVER(PARTITION BY CLASSNUMBER ) TOTALSCORE
    FROM CLASSINFO ORDER BY CLASSNUMBER;
    
    --結果與上述相同, 就不再展示

    通過 PARTITION BY CLASSNUMBER 進行分組

  3. 讓我們嘗試下面幾種用法, 就不在這張表上做操作:

    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
  4. 小結

    SUM() Over(ORDER BY COLUMN_NAME) 連續累加

    SUM() Over(ORDER BY COLUMN_NAME DESC) 倒序累加

    SUM() Over() 全部求和

  5. 註意

    但需要註意的一個地方是: 在這裏使用 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, 對求得的結果集進行累加.

  6. 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 再度進行累加操作.

  7. 總結

    在 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函數