zoukankan      html  css  js  c++  java
  • 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');
    

    从这里分别观察其特性:

    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() 的讲述也差不多告一段落.

  • 相关阅读:
    讲解开源项目:一步步跑起来个 Java 前后端分离的人力资源管理系统
    HelloDjango 第 12 篇:解锁博客侧栏,GoGoGo!
    HelloDjango 第 11 篇:自动生成文章摘要
    Python 命令行之旅:深入 argparse(二)
    MongoDB 复制机制
    GridView自定义分页
    接口分离原则
    依赖倒置原则
    访问 IIS 元数据库失败 的解决方法
    开放封闭原则
  • 原文地址:https://www.cnblogs.com/zyzdisciple/p/8001538.html
Copyright © 2011-2022 走看看