DENSE_RANK() 或 ROW_NUMBER() - 创建分组

Posted

技术标签:

【中文标题】DENSE_RANK() 或 ROW_NUMBER() - 创建分组【英文标题】:DENSE_RANK() or ROW_NUMBER() - to create groupings 【发布时间】:2017-11-01 14:15:15 【问题描述】:

我正在尝试使用 DENSE_RANK() OVER PARTITION 为某些数据创建一组排名。数据按日期、日期时间和状态排序。它是我需要对其进行排名的日期时间和状态,下面是所需的排名输出。

我使用了各种 DENSE_RANK 和 ROW_NUMBER 组合,但无法获得正确的分组。 是否有可能获得我需要的排名?

【问题讨论】:

您可以使用senseful.github.io/text-table来创建文本表格,而不是使用屏幕截图。 【参考方案1】:

使用lag()case 表达式将ctstatus 与之前的ctstatus 进行比较,当不同时返回1。这是假设您要重置每人每天的排名,如果不将它们从partition by 移动到over() 子句的order by 的开头。然后使用sum() over() 作为rnk 的运行总数。

;with cte as (
  select t.*
    /* compare ctstatus with previous ctstatus, when different return by 1 */
    , inc = case when ctstatus = lag(ctstatus) over (
      partition by 
          description -- for each person
        , atdate --for each day ? 
      order by atdatetime 
      ) then 0 else 1 end
  from t
)
select 
    atdatetime
  , atdate
  , atext
  , description
  , ctstatus
  , rnk = sum(inc) over (partition by description, atdate order by atdatetime)
from cte;

【讨论】:

【参考方案2】:

根据您的描述和示例数据,不可能知道要按什么进行分组或分区,所以我暂时忽略了这方面的内容。也就是说,这就是您如何使用SUM() OVER 窗口聚合来做到这一点。

样本数据

if object_id('tempdb..#x') is not null drop table #x;

create table #x
(
  ATDATETIME datetime, 
  ATDATE date, 
  ATEXT varchar(10),
  CTSTATUS varchar(30)
);
create clustered index cl_x on #x(ATDATETIME); -- for a sort-free execution plan

insert #x values
(getdate()-1.90, cast(getdate()-1.90 as date), 1397, 'BK tomorrow...'),
(getdate()-1.85, cast(getdate()-1.85 as date), 1397, 'AVAILABLE'),
(getdate()-1.83, cast(getdate()-1.83 as date), 1397, 'AVAILABLE'),
(getdate()-1.81, cast(getdate()-1.81 as date), 1397, 'AVAILABLE'),
(getdate()-1.75, cast(getdate()-1.75 as date), 1397, 'AVAILABLE'),
(getdate()-1.71, cast(getdate()-1.71 as date), 1397, 'Went away'),
(getdate()-1.67, cast(getdate()-1.67 as date), 1397, 'AVAILABLE'),
(getdate()-1.63, cast(getdate()-1.63 as date), 1397, 'AVAILABLE'),
(getdate()-1.60, cast(getdate()-1.60 as date), 1397, 'AVAILABLE'),
(getdate()-1.56, cast(getdate()-1.56 as date), 1397, 'AVAILABLE'),
(getdate()-1.55, cast(getdate()-1.55 as date), 1397, 'Snuck out for beer'),
(getdate()-1.50, cast(getdate()-1.50 as date), 1397, 'AVAILABLE'),
(getdate()-1.46, cast(getdate()-1.46 as date), 1397, 'AVAILABLE');

解决方案

select ATDATETIME, ATDATE, ATEXT, CTSTATUS, RNK =
  SUM(IIF(CTSTATUS = 'AVAILABLE' AND CTSTATUS_LAG1 = 'AVAILABLE',0,1))
    OVER (ORDER BY ATDATETIME)
from
(
  select *, CTSTATUS_LAG1 = LAG(CTSTATUS, 1) OVER (ORDER BY ATDATETIME)
  from #x
) tlag;

结果

ATDATETIME              ATDATE     ATEXT      CTSTATUS                       RNK
----------------------- ---------- ---------- ------------------------------ -----------
2017-10-30 14:00:11.990 2017-10-30 1397       BK tomorrow...                 1
2017-10-30 15:12:11.990 2017-10-30 1397       AVAILABLE                      2
2017-10-30 15:40:59.990 2017-10-30 1397       AVAILABLE                      2
2017-10-30 16:09:47.990 2017-10-30 1397       AVAILABLE                      2
2017-10-30 17:36:11.990 2017-10-30 1397       AVAILABLE                      2
2017-10-30 18:33:47.990 2017-10-30 1397       Went away                      3
2017-10-30 19:31:23.990 2017-10-30 1397       AVAILABLE                      4
2017-10-30 20:28:59.990 2017-10-30 1397       AVAILABLE                      4
2017-10-30 21:12:11.990 2017-10-30 1397       AVAILABLE                      4
2017-10-30 22:09:47.990 2017-10-30 1397       AVAILABLE                      4
2017-10-30 22:24:11.990 2017-10-30 1397       Snuck out for beer             5
2017-10-30 23:36:11.990 2017-10-30 1397       AVAILABLE                      6
2017-10-31 00:33:47.990 2017-10-31 1397       AVAILABLE                      6

【讨论】:

以上是关于DENSE_RANK() 或 ROW_NUMBER() - 创建分组的主要内容,如果未能解决你的问题,请参考以下文章

sql排序对比(row_number,rank,dense_rank)

[转]oracle分析函数Rank, Dense_rank, row_number

好用的排名函数~ROW_NUMBER(),RANK(),DENSE_RANK() 三兄弟

窗口函数rank, dense_rank, row_number的区别

窗口函数rank, dense_rank, row_number的区别

row_number() rank() dense_rank()