DENSE_RANK 在两列上,其中一列是不同的值,另一列有重复

DENSE_RANK on two columns, where one column are distinct values and the other has duplicate

我有以下情况,我正在努力了解如何应用 DENSE_RANK() 来获得我想要的结果:

ID Date Value
1 1990-05-17 1.00
1 1991-10-12 1.00
1 1992-08-01 1.00
1 1993-07-05 0.67
1 1994-05-02 0.67
1 1995-02-01 1.00
1 1996-03-01 1.00

根据上述数据,我尝试使用 DateValue 列的组合来识别不同的时期,其中从 Value 列从一个值更改为另一个值。这是我正在寻找的结果:

ID Date Value Period
1 1990-05-17 1.00 1
1 1991-10-12 1.00 1
1 1992-08-01 1.00 1
1 1993-07-05 0.67 2
1 1994-05-02 0.67 2
1 1995-02-01 1.00 3
1 1996-03-01 1.00 3

如您所见,有 3 个不同的时期。我遇到的问题是,当我使用 DENSE_RANK() 时,我得到以下两种结果之一:

SELECT DENSE_RANK() OVER (PARTITION BY ID ORDER BY Date, Value)

ID Date Value Period
1 1990-05-17 1.00 1
1 1991-10-12 1.00 2
1 1992-08-01 1.00 3
1 1993-07-05 0.67 4
1 1994-05-02 0.67 5
1 1995-02-01 1.00 6
1 1996-03-01 1.00 7

SELECT DENSE_RANK() OVER (PARTITION BY ID ORDER BY Value)

ID Date Value Period
1 1990-05-17 1.00 1
1 1991-10-12 1.00 1
1 1992-08-01 1.00 1
1 1993-07-05 0.67 2
1 1994-05-02 0.67 2
1 1995-02-01 1.00 1
1 1996-03-01 1.00 1

如您所见,问题出在 Date 列上,因为我需要它作为一个累积周期。此外,周期的数量从 IDID 不等,并且 Date 列背后没有一致的科学依据。例如,一个成员可以在一年内有两个条目。

这被称为间隙和孤岛问题。一种方法是使用几个 ROW_NUMBER 将您的数据分组:


WITH CTE AS(
    SELECT *,
           ROW_NUMBER() OVER (PARTITION BY ID ORDER BY [date],[value])-
           ROW_NUMBER() OVER (PARTITION BY ID, [value] ORDER BY [date]) AS Grp
    FROM (VALUES(1,CONVERT(date,'1990-05-17'),1.00),
                (1,CONVERT(date,'1991-10-12'),1.00),
                (1,CONVERT(date,'1992-08-01'),1.00),
                (1,CONVERT(date,'1993-07-05'),0.67),
                (1,CONVERT(date,'1994-05-02'),0.67),
                (1,CONVERT(date,'1995-02-01'),1.00),
                (1,CONVERT(date,'1996-03-01'),1.00))V(ID,Date,Value))
SELECT ID,
       Date,
       Value,
       DENSE_RANK() OVER (PARTITION BY ID ORDER BY Grp) AS Period
FROM CTE;

您可以使用 LAG() window 函数为每一行获取其先前的值,并使用 SUM() window 函数的条件聚合获取 Periods :

SELECT ID, Date, Value,
       SUM(CASE WHEN VALUE = prev_value THEN 0 ELSE 1 END) OVER (PARTITION BY ID ORDER BY Date) Period 
FROM (
  SELECT *, LAG(Value) OVER (PARTITION BY ID ORDER BY Date) prev_value
  FROM tablename
) t
ORDER BY Date;

参见demo