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的区别