zoukankan      html  css  js  c++  java
  • Oracle分析函数-排序排列(rank、dense_rank、row_number、ntile)

    (1)rank函数返回一个唯一的值,除非遇到相同的数据时,此时所有相同数据的排名是一样的,同时会在最后一条相同记录和下一条不同记录的排名之间空出排名。
    (2)dense_rank函数返回一个唯一的值,除非当碰到相同数据时,此时所有相同数据的排名都是一样的。
    (3)row_number函数返回一个唯一的值,当碰到相同数据时,排名按照记录集中记录的顺序依次递增。
    (4)ntile是要把查询得到的结果平均分为几组,如果不平均则分给第一组。

    例如:

    create table s_score
    (   s_id number(6)
       ,score number(4,2)
    );
    insert into s_score values(001,98);
    insert into s_score values(002,66.5);
    insert into s_score values(003,99);
    insert into s_score values(004,98);
    insert into s_score values(005,98);
    insert into s_score values(006,80);
    
    select
        s_id 
       ,score
       ,rank() over(order by score desc) rank               --按照成绩排名,纯排名
       ,dense_rank() over(order by score desc) dense_rank   --按照成绩排名,相同成绩排名一致
       ,row_number() over(order by score desc) row_number   --按照成绩依次排名
       ,ntile(3) over (order by score desc) group_s         --按照分数划分成绩梯队
    from s_score;

    排名/排序的时候,有时候,我们会想到利用伪列row_num,利用row_num确实可以解决某些场景下的问题(但是相对也比较复杂),而且有些场景下的问题却很难解决。

    例:取成绩前三名,并且前三名含有并列的情况。通过上面例子,我们可以直观的看到,结果应该有5条记录:

    select
        s_id 
       ,score
       ,dense_rank
    from (
    select
        s_id 
       ,score
       ,rank() over(order by score desc) rank
       ,dense_rank() over(order by score desc) dense_rank
       ,row_number() over(order by score desc) row_number
    from s_score
    ) t
    where dense_rank <= 3;
    
       S_ID  SCORE DENSE_RANK
    ------- ------ ----------
          3  99.00          1
          1  98.00          2
          5  98.00          2
          4  98.00          2
          6  80.00          3
          

    如果只是简单的想到去用rownum <= 3 得到的结果显然不可能是正确的。

    组内的排名或者排序是经常遇到的一种场景。
    例如,取每个销售部门内,销售业绩最好的前三名。取每个班级内成绩排名信息等等..
    取每个班级内每门课成绩排名第一的同学信息:

    drop table S_SCORE;
    create table S_SCORE
    (
      S_ID  NUMBER(6),
      CLASS_ID VARCHAR2(2),
      COURSE VARCHAR2(20),
      SCORE NUMBER(5,2)
    );
    
    INSERT INTO S_SCORE VALUES(1001,'A','MATH','67');
    INSERT INTO S_SCORE VALUES(1004,'B','MATH','88');
    INSERT INTO S_SCORE VALUES(1002,'A','MATH','99');
    INSERT INTO S_SCORE VALUES(1003,'A','MATH','55');
    INSERT INTO S_SCORE VALUES(1001,'B','MATH','88');
    INSERT INTO S_SCORE VALUES(1001,'B','MATH','70');
    INSERT INTO S_SCORE VALUES(1001,'A','ORACLE','97');
    INSERT INTO S_SCORE VALUES(1004,'B','ORACLE','48');
    INSERT INTO S_SCORE VALUES(1002,'A','ORACLE','79');
    INSERT INTO S_SCORE VALUES(1003,'A','ORACLE','65');
    INSERT INTO S_SCORE VALUES(1001,'B','ORACLE','82');
    INSERT INTO S_SCORE VALUES(1001,'B','ORACLE','78');
    
    select
       s_id
      ,class_id
      ,course
      ,score
      ,dense_rank() over (partition by class_id,course order by score desc) drk
    from S_SCORE;
    
       S_ID CLASS_ID COURSE                 SCORE        DRK
    ------- -------- -------------------- ------- ----------
       1002 A        MATH                   99.00          1
       1001 A        MATH                   67.00          2
       1003 A        MATH                   55.00          3
       1001 A        ORACLE                 97.00          1
       1002 A        ORACLE                 79.00          2
       1003 A        ORACLE                 65.00          3
       1004 B        MATH                   88.00          1
       1001 B        MATH                   88.00          1
       1001 B        MATH                   70.00          2
       1001 B        ORACLE                 82.00          1
       1001 B        ORACLE                 78.00          2
       1004 B        ORACLE                 48.00          3
       
    select
       s_id
      ,class_id
      ,course
      ,score
    from (
    select
       s_id
      ,class_id
      ,course
      ,score
      ,dense_rank() over (partition by class_id,course order by score desc) drk
    from S_SCORE
    ) t
    where drk = 1;
    
    
       S_ID CLASS_ID COURSE                 SCORE
    ------- -------- -------------------- -------
       1002 A        MATH                   99.00
       1001 A        ORACLE                 97.00
       1004 B        MATH                   88.00
       1001 B        MATH                   88.00
       1001 B        ORACLE                 82.00   

    rank()和dense_rank()用法相似,这里就不在举例说明了。可以将上面的例子中dense_rank()替换成rank()实现。

    接下来,看一个使用row_number()的场景
    例:查看每个部门最近一笔销售记录:

    select * from criss_sales order by dept_id,sale_date desc;
    
    DEPT_ID SALE_DATE   GOODS_TYPE    SALE_CNT
    ------- ----------- ---------- -----------
    D01     2014/5/4    G02                 80
    D01     2014/4/30   G03                800
    D01     2014/4/8    G01                200
    D01     2014/3/4    G00                700
    D02     2014/5/2    G03                900
    D02     2014/4/27   G01                300
    D02     2014/4/8    G02                100
    D02     2014/3/6    G00                500

    即,我们希望得到这两条记录:

    D01     2014/5/4    G02                 80
    D02     2014/5/2    G03                900
    select
      dept_id
     ,sale_date
     ,goods_type
     ,sale_cnt
     ,row_number() over (partition by dept_id order by sale_date desc)
    from criss_sales;
    
    DEPT_ID SALE_DATE   GOODS_TYPE    SALE_CNT ROW_NUMBER()OVER(PARTITIONBYDE
    ------- ----------- ---------- ----------- ------------------------------
    D01     2014/5/4    G02                 80                              1
    D01     2014/4/30   G03                800                              2
    D01     2014/4/8    G01                200                              3
    D01     2014/3/4    G00                700                              4
    D02     2014/5/2    G03                900                              1
    D02     2014/4/27   G01                300                              2
    D02     2014/4/8    G02                100                              3
    D02     2014/3/6    G00                500                              4
    
    select
      dept_id
     ,sale_date
     ,goods_type
     ,sale_cnt
    from (
    select
      dept_id
     ,sale_date
     ,goods_type
     ,sale_cnt
     ,row_number() over (partition by dept_id order by sale_date desc) rn
    from criss_sales
    ) t
    where rn = 1;
    
    DEPT_ID SALE_DATE   GOODS_TYPE    SALE_CNT
    ------- ----------- ---------- -----------
    D01     2014/5/4    G02                 80
    D02     2014/5/2    G03                900

    有时会有这样的需求:如果数据排序后分为三部分,业务人员只关心其中的一部分,如何将这中间的三分之一数据拿出来呢?
    这时比较好的选择,就是使用ntile函数:

    select
          dept_id
         ,sale_date
         ,goods_type
         ,sale_cnt
         ,ntile(3) over (order by sale_cnt desc nulls last)  all_cmp
         ,ntile(3) over (partition by dept_id order by sale_cnt desc nulls last) all_dept
    from criss_sales;

    可以看到,Ntile函数为各个记录在记录集中的排名计算比例,返回每条记录所在集合比例位置的值。
    例如我们关心全公司前三分之一部分的数据,只需选择 ALL_CMP = 1 的数据就可以了;
    如果只是关心全公司中间的三分之一数据,只需选择 ALL_CMP = 2 的数据就可以了。

  • 相关阅读:
    一款漂亮的表格样式(简约版)
    性能测试工具 Jmeter GET 请求 参数为 Json 串且参数中存在变量的转化
    _groovy
    Linux SElinux
    Jmeter-后置处理器(Json extractor)
    jmeter处理接口加密和解密
    nmon内存分析
    jmeter 非GUI执行测试,没有响应数据保存到jtl文件办法
    如何隐藏电脑里的文件或者文件夹
    详解JMeter函数和变量
  • 原文地址:https://www.cnblogs.com/sooner/p/7727242.html
Copyright © 2011-2022 走看看