按顺序排列的密集等级

Posted

技术标签:

【中文标题】按顺序排列的密集等级【英文标题】:Dense Rank with order by 【发布时间】:2017-03-16 17:10:48 【问题描述】:

我有这样的分配表

EMPLID | RCD | COMPANY |   EFFDT       |  SALARY
---------------------------------------------------
100    | 0   | xyz     |   1/1/2000    |    1000
100    | 0   | xyz     |   1/15/2000   |    1100
100    | 0   | xyz     |   1/31/2000   |    1200
100    | 0   | ggg     |   2/15/2000   |    1500
100    | 1   | abc     |   3/1/2000    |    2000
100    | 1   | abc     |   4/1/2000    |    2100

我需要一个计数器,只要 RCD 或公司组合发生变化,它就会增加,并且应该由 effdt 订购。

EMPLID | RCD | COMPANY |   EFFDT       |  SALARY     | COUNTER
-------|-----|---------|---------------|-------------|----------
100    | 0   | xyz     |   1/1/2000    |    1000     | 1
100    | 0   | xyz     |   1/15/2000   |    1100     | 1
100    | 0   | xyz     |   1/31/2000   |    1200     | 1
100    | 0   | ggg     |   2/15/2000   |    1500     | 2
100    | 1   | abc     |   3/1/2000    |    2000     | 3
100    | 1   | abc     |   4/1/2000    |    2100     | 3

我尝试了按 EMPLID、RCD、COMPANY 排序的 Dense_Rank 函数,它为我提供了计数器,但它不是按 effdt 排序的。

SELECT EMPLID,RCD,COMPANY,EFFDT,
    DENSE_RANK() over (order by EMPLID , RCD , COMPANY) AS COUNTER
FROM ASSIGNMENT ;

按 EFFDT 排序,给出增量计数器 1 ... 6

SELECT EMPLID,RCD,COMPANY,EFFDT,
  DENSE_RANK() over (order by EFFDT) AS COUNTER 
FROM ASSIGNMENT;

请帮助我找出我缺少的东西。

【问题讨论】:

只要您的数据顺序相同,Dense_rank 将保持相同的数字...所以如您所见,如果您按日期进行密集排名,它会给出 1 到 6...只需使用 'dense_rank( )作为计数器(按公司订购),并在 EFFDT 的选择查询订单结束时......它会给你你想要的 如果表中的下一行(对于EMPLID=100, EFFDT=5/1/2000)具有RCD=0, COMPANY=xyz 的组合,那么所需的输出是什么?计数器应该返回 1,还是应该分配一个新值 4? @mathguy 应该是 1 @Veljko89 - 我最后尝试通过 EFFDT 订购,但它仍然会将等级 1 提供给 (0,ggg) 和 2 给 (0,xyz),因为它是由 emplid 、rcd 和公司订购的首先,不是effdt。只有通过 EFFDT 下单的差异才会使显示的结果将由 EFFDT 下单。所以 Rank 2 将首先显示,然后显示 1。 @mathguy - 我有另一个要求,如果 RCD 和 COMPANY 组合重复,我需要排名为 4 。你能帮我解决一下吗。 【参考方案1】:

这应该有效 - 澄清 rcd 和 company 的组合应该保持相同的“计数器”,即使它出现在非连续期间。我在测试数据中添加了更多行以确保得到正确的结果。

与 Serg 的解决方案(回答不同的问题)一样,该解决方案对基础数据进行一次传递,然后对第一次传递的结果进行第二次传递(全部在内存中,因此应该相对较快)。没有办法解决这个问题 - 这需要两个不同的分析函数,其中一个依赖于另一个的结果,并且不允许嵌套分析函数。 (这部分答案涉及 OP 对 Serg 的回答的评论。)

with
     test_data ( emplid, rcd, company, effdt, salary ) as (
       select 100, 0, 'xyz', to_date('1/1/2000' , 'mm/dd/yyyy'), 1000 from dual union all
       select 100, 0, 'xyz', to_date('1/15/2000', 'mm/dd/yyyy'), 1100 from dual union all
       select 100, 0, 'xyz', to_date('1/31/2000', 'mm/dd/yyyy'), 1200 from dual union all
       select 100, 0, 'ggg', to_date('2/15/2000', 'mm/dd/yyyy'), 1500 from dual union all
       select 100, 1, 'abc', to_date('3/1/2000' , 'mm/dd/yyyy'), 2000 from dual union all
       select 100, 1, 'abc', to_date('4/1/2000' , 'mm/dd/yyyy'), 2100 from dual union all
       select 100, 0, 'xyz', to_date('5/1/2000' , 'mm/dd/yyyy'), 2200 from dual union all
       select 100, 1, 'ggg', to_date('8/15/2000', 'mm/dd/yyyy'), 2300 from dual
     )
-- end of test data; the actual solution (SQL query) begins below this line
select emplid, rcd, company, effdt, salary,
       dense_rank() over (partition by emplid order by min_dt) as counter
from ( select emplid, rcd, company, effdt, salary, 
              min(effdt) over (partition by emplid, rcd, company) as min_dt
       from   test_data )
order by effdt                --   ORDER BY is optional
;

    EMPLID        RCD COM EFFDT                   SALARY    COUNTER
---------- ---------- --- ------------------- ---------- ----------
       100          0 xyz 2000-01-01 00:00:00       1000          1
       100          0 xyz 2000-01-15 00:00:00       1100          1
       100          0 xyz 2000-01-31 00:00:00       1200          1
       100          0 ggg 2000-02-15 00:00:00       1500          2
       100          1 abc 2000-03-01 00:00:00       2000          3
       100          1 abc 2000-04-01 00:00:00       2100          3
       100          0 xyz 2000-05-01 00:00:00       2200          1
       100          1 ggg 2000-08-15 00:00:00       2300          4

 8 rows selected

【讨论】:

【参考方案2】:

尝试滞后

WITH flagged AS (  
    SELECT *, 
      CASE WHEN LAG(RCD) OVER(PARTITION BY EMPLID ORDER BY EFFDT) = RCD 
              AND LAG(COMPANY) OVER(PARTITION BY EMPLID ORDER BY EFFDT) = COMPANY THEN 0 ELSE 1 END strtFlag
    FROM tbl
    )

SELECT EMPLID, RCD, COMPANY, EFFDT, SALARY, SUM(strtFlag) OVER(PARTITION BY EMPLID ORDER BY EFFDT) COUNTER
FROM flagged

或者,使用组的 DENSE_RANK()

WITH grps AS (  
    SELECT *, 
      ROW_NUMBER() OVER(PARTITION BY EMPLID ORDER BY EFFDT) -
      ROW_NUMBER() OVER(PARTITION BY EMPLID, RCD, COMPANY ORDER BY EFFDT) grp
    FROM tbl
    )

SELECT EMPLID, RCD, COMPANY, EFFDT, SALARY
  , DENSE_RANK() OVER(PARTITION BY EMPLID ORDER BY grp) COUNTER
FROM grps

无论如何看起来需要两个步骤来获得密集编号。

【讨论】:

感谢您的回复。两步过程可能有效,但我不能使用它,因为示例问题很简单,但我需要容纳它的程序,不能使用 2 步 @bhushan - 为什么“两步”解决方案不起作用?我相信你认为这行不通;你可能错了,但让我们弄清楚。在任何情况下,Serg 的解决方案都是正确的,但一方面 - 如果您在以后再次使用 0、xyz 组合,它会给它一个新的计数器值,它不会识别它仍然是旧的 1。 (这就是为什么我在评论中问你并且你澄清了 - 首先要完全理解要求。)【参考方案3】:

我想你正在寻找:

SELECT EMPLID,RCD,COMPANY,EFFDT,
    DENSE_RANK() over (order by EMPLID , RCD , COMPANY) AS COUNTER
FROM (select * from ASSIGNMENT order by EFFDT);

SELECT EMPLID,RCD,COMPANY,EFFDT,
    DENSE_RANK() over (order by EMPLID , RCD , COMPANY) AS COUNTER
FROM (select * from ASSIGNMENT order by EMPLID , RCD , COMPANY, EFFDT);

【讨论】:

我尝试在最后通过 EFFDT 订购,但它仍然会将等级 1 赋予 (0,ggg) 和 2 至 (0,xyz),因为它首先由 emplid 、rcd 和公司订购,而不是效果。只有通过 EFFDT 下单的差异才会使显示的结果将由 EFFDT 下单。所以 Rank 2 将首先显示,然后显示 1。

以上是关于按顺序排列的密集等级的主要内容,如果未能解决你的问题,请参考以下文章

parent数组成等级菜单全展开式顺序排列

我使用密集等级函数为学生生成随机排名。请给我代码,只提取前5%的学生

excel中怎么将英文单词按字母顺序排序?

密集索引和稀疏索引的区别

按字母顺序排列的字典键[重复]

用单词和数字按字母顺序排列字符串