【问题标题】:Oracle/SQL - Using the rank functionOracle/SQL - 使用排名函数
【发布时间】:2011-11-15 17:02:16
【问题描述】:

我正在尝试做的是一张表格中的人员列表,如果一个人存在不止一次,则返回他们的记录,其中包含排名最高的“代码”

代码排名(从高到低):T、E、F

所以对于给定的数据集

Person  Code
----------------    
Tom     F
Paul    E
Mark    F
Paul    T
Mark    E
Chris   T
Chris   E

我会从我的查询中得到以下信息

Person  Code
----------------
Tom     F
Paul    T
Mark    E
Chris   T

我假设这将使用排名/分析函数,但我对它们还不够熟悉。

谢谢!

【问题讨论】:

    标签: sql oracle rank


    【解决方案1】:

    您可以使用RANK函数对数据进行排名

    SQL> ed
    Wrote file afiedt.buf
    
      1  with data as (
      2    select 'Tom' person, 'F' code from dual union all
      3    select 'Paul', 'E' from dual union all
      4    select 'Paul', 'T' from dual union all
      5    select 'Mark', 'F' from dual union all
      6    select 'Mark', 'E' from dual
      7  )
      8  select *
      9    from (select person,
     10                 code,
     11                 rank() over (partition by person
     12                                  order by (case when code='T' then 1
     13                                                 when code='E' then 2
     14                                                 when code='F' then 3
     15                                                 else null
     16                                             end)) rnk
     17*           from data)
    SQL> /
    
    PERS C        RNK
    ---- - ----------
    Mark E          1
    Mark F          2
    Paul T          1
    Paul E          2
    Tom  F          1
    
    Elapsed: 00:00:00.00
    

    然后,您只需要选择 RNK 为 1 的行

    SQL> ed
    Wrote file afiedt.buf
    
      1  with data as (
      2    select 'Tom' person, 'F' code from dual union all
      3    select 'Paul', 'E' from dual union all
      4    select 'Paul', 'T' from dual union all
      5    select 'Mark', 'F' from dual union all
      6    select 'Mark', 'E' from dual
      7  )
      8  select *
      9    from (select person,
     10                 code,
     11                 rank() over (partition by person
     12                                  order by (case when code='T' then 1
     13                                                 when code='E' then 2
     14                                                 when code='F' then 3
     15                                                 else null
     16                                             end)) rnk
     17            from data)
     18*  where rnk = 1
    SQL> /
    
    PERS C        RNK
    ---- - ----------
    Mark E          1
    Paul T          1
    Tom  F          1
    
    Elapsed: 00:00:00.00
    

    【讨论】:

    • 使用row_number() 而不是rank() 如果(人,代码)可以重复,并且您只希望返回一个(任意选择的)行。如果(人,代码)可以重复,并且您想要单行并且您关心多行中的哪一行,请向order by 添加其他条件。您可能想考虑将代码存储到表中的 rank_value 并加入,而不是将其编码到存储过程中。在某些情况下,好处会超过额外表的缺点。
    【解决方案2】:

    最短、性能最高的 Oracle 专用解决方案:

    SQL> create table mytable(person,code)
      2  as
      3  select 'Tom', 'F' from dual union all
      4  select 'Paul', 'E' from dual union all
      5  select 'Mark', 'F' from dual union all
      6  select 'Paul', 'T' from dual union all
      7  select 'Mark', 'E' from dual union all
      8  select 'Chris', 'T' from dual union all
      9  select 'Chris', 'E' from dual
     10  /
    
    Table created.
    
    SQL> select person
      2       , max(code) keep (dense_rank first order by decode(code,'T',1,'E',2,'F',3,4)) code
      3    from mytable
      4   group by person
      5  /
    
    PERSO C
    ----- -
    Chris T
    Mark  E
    Paul  T
    Tom   F
    
    4 rows selected.
    

    问候,
    抢。

    【讨论】:

      【解决方案3】:

      我不认为 RANK 是你需要的......

      基本上,您的删除将如下所示:(伪查询)

      delete the rows from person
      where that row is not in ( select the rows from person with the highest code )
      

      编辑

      这个技巧也可能对你有帮助:

      select person, code, decode( code, 'T', 1, 'E', 2, 'F', 3, 0 ) from mytable
      

      【讨论】:

      • 删除?我认为他想查询最大排名,而不是删除数据。
      • 正确我不想删除 - 只需要为表中的每个人返回一个结果。如果一个人被列出不止一次,则返回具有最高“代码”排名的记录。我可以看到“修剪”可能会产生误导。
      【解决方案4】:

      嗯...标准 SQL 的替代建议。 有一个CODE_WEIGHT 表,例如:

      CODE WEIGHT
      T    3    
      E    2
      F    1
      

      然后按人员对您的查询进行分组(如果这是分组标准)并选择包含max(weight) 的不同代码。

      我会在几分钟后发布查询。

      更新

      好的,抱歉耽搁了。

      这是使用前面提到的表格和@Randy 技巧的解决方案:

      SELECT 
       pp.person, decode(max(c.weight), 3, 'T', 2, 'E', 1, 'F', '') code
      FROM 
       person pp INNER JOIN code_weight c on (pp.code = c.code)
      GROUP BY
       pp.person 
      ORDER BY 
       person DESC;
      

      我很确定有一种方法可以转储 Oracle 专有功能并在纯 SQL 中完成任务...无论如何,既然您要求使用 Oracle 解决方案,那就是。

      更新 2

      正如所承诺的,这是我能想到的最好的标准 SQL 版本:

      SELECT 
       p.person, c.code
      FROM
      (
       SELECT 
         pp.person, MAX(cc.weight) weight
       FROM 
        person pp INNER JOIN code_weight cc ON (pp.code = cc.code)
       GROUP BY
        pp.person 
      ) p INNER JOIN code_WEIGHT c ON (p.weight = c.weight)
      ORDER BY
        p.person DESC;
      

      这两个连接有点难看......但它在没有专有扩展的情况下完成了这项工作。有哪位 SQL 高手知道如何优化它?

      干杯,

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-07-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-23
        • 2012-04-19
        相关资源
        最近更新 更多