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

时间:2017-11-01 14:15:16

标签: sql sql-server tsql sql-server-2016

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

enter image description here

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

2 个答案:

答案 0 :(得分:1)

使用lag()case表达式将ctstatus与之前的ctstatus进行比较,并在不同时返回1。这假设您要重置每个人和每天的排名,如果不是将其从partition by移动到order by子句的over()的开头。然后使用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;

答案 1 :(得分:0)

根据您的描述和示例数据,我们无法知道要分组或分区的内容,所以我现在忽略了这一点。也就是说,您可以使用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