使用唯一 ID 对日期范围和岛屿进行分组,无需按分隔符进行分区
Grouping date-ranges and Islands with uniq ID's without Partition by seperator
我需要一些关于基于日期范围分组的帮助,我没有任何明显的分隔符分区。我有这个数据集 (Table New_Test),其中每个 ID 都有未知数量的月+年条目,但如果它们是连接的,它们应该获得相同的分组 ID(在新列中)。例如。如果 2016 年第 9 个月之后是 2016 年第 10 个月,它们都应该获得 Grouping-ID 1。如果存在差距,即 2018 年第 3 个月到 2018 年第 5 个月的情况,则新的 Grouping ID 应该是分配。
为了找到月份的连续顺序,我绘制了 Match1 和 Match2(辅助列),它们是前一个和下一个 Start- 和 EndDate 的超前和滞后函数。
为了分配 Grouping-ID,我尝试了一个 IIF 公式,其中 StartDate = Match2 或 EndDate = Match1 之间的匹配被分配值 1 或 0。我尝试用各种替代 1 Dense_rank、排名、Row_number 的版本。如果我使用 Dense_Rank() OVER (PARTITION BY ID ORDER BY ID),我得到关于 ID 1 的三个组范围的 Grouping-ID 值 1,0,1 而不是 1,2,3 的目标因为我的数据集中没有可用的分隔符。这意味着当我稍后想按日期范围 MIN 和 MAX 对这些数据进行分组时,这两个岛屿将合并为 1 个,这不是我想要的。
我希望有人对此有一些很好的意见! :)
SELECT
ID
,StartDate
,EndDate
,LEAD(DATEADD(day,-1,StartDate),1) OVER (ORDER BY ID, Year, Month) AS Match1
,LAG(DATEADD(day,1,EndDate),1) OVER (ORDER BY ID, Year, Month) AS Match2
,IIF(StartDate= LAG(DATEADD(day,1,EndDate),1) OVER (ORDER BY ID, Year, Month)
OR EndDate =LEAD(DATEADD(day,-1,StartDate),1) OVER (ORDER BY ID, Year, Month)
,1,0) AS Grouping-ID
,Year
,Month
FROM NEW_Test
我的数据在第一次编辑后的样子:
ID StartDate EndDate Match1 Match2 Year Month
1 01-09-2016 30-09-2016 30-09-2016 NULL 2016 9
1 01-10-2016 31-10-2016 31-10-2016 01-10-2016 2016 10
1 01-11-2016 30-11-2016 30-11-2016 01-11-2016 2016 11
1 01-12-2016 31-12-2016 31-12-2016 01-12-2016 2016 12
1 01-01-2017 31-01-2017 31-01-2017 01-01-2017 2017 1
1 01-02-2017 28-02-2017 28-02-2017 01-02-2017 2017 2
1 01-03-2017 31-03-2017 31-03-2017 01-03-2017 2017 3
1 01-04-2017 30-04-2017 30-04-2017 01-04-2017 2017 4
1 01-05-2017 31-05-2017 31-05-2017 01-05-2017 2017 5
1 01-06-2017 30-06-2017 30-06-2017 01-06-2017 2017 6
1 01-07-2017 31-07-2017 31-07-2017 01-07-2017 2017 7
1 01-08-2017 31-08-2017 31-08-2017 01-08-2017 2017 8
1 01-09-2017 30-09-2017 30-09-2017 01-09-2017 2017 9
1 01-10-2017 31-10-2017 31-10-2017 01-10-2017 2017 10
1 01-11-2017 30-11-2017 30-11-2017 01-11-2017 2017 11
1 01-12-2017 31-12-2017 31-12-2017 01-12-2017 2017 12
1 01-01-2018 31-01-2018 31-01-2018 01-01-2018 2018 1
1 01-02-2018 28-02-2018 28-02-2018 01-02-2018 2018 2
1 01-03-2018 31-03-2018 30-04-2018 01-03-2018 2018 3
1 01-05-2018 31-05-2018 31-10-2018 01-04-2018 2018 5
1 01-11-2018 30-11-2018 30-11-2018 01-06-2018 2018 11
1 01-12-2018 31-12-2018 NULL 01-12-2018 2018 12
2 01-09-2016 30-09-2016 30-09-2016 NULL 2016 9
2 01-10-2016 31-10-2016 31-10-2016 01-10-2016 2016 10
2 01-11-2016 30-11-2016 30-11-2016 01-11-2016 2016 11
2 01-01-2017 31-01-2017 31-01-2017 01-01-2017 2017 1
2 01-02-2017 28-02-2017 28-02-2017 01-02-2017 2017 2
2 01-03-2017 31-03-2017 31-03-2017 01-03-2017 2017 3
2 01-04-2017 30-04-2017 30-04-2017 01-04-2017 2017 4
2 01-05-2017 31-05-2017 31-05-2017 01-05-2017 2017 5
最终结果应该是什么:
ID StartDate EndDate Match1 Match2 Year Month Grouping-ID
1 01-09-2016 30-09-2016 30-09-2016 NULL 2016 9 1
1 01-10-2016 31-10-2016 31-10-2016 01-10-2016 2016 10 1
1 01-11-2016 30-11-2016 30-11-2016 01-11-2016 2016 11 1
1 01-12-2016 31-12-2016 31-12-2016 01-12-2016 2016 12 1
1 01-01-2017 31-01-2017 31-01-2017 01-01-2017 2017 1 1
1 01-02-2017 28-02-2017 28-02-2017 01-02-2017 2017 2 1
1 01-03-2017 31-03-2017 31-03-2017 01-03-2017 2017 3 1
1 01-04-2017 30-04-2017 30-04-2017 01-04-2017 2017 4 1
1 01-05-2017 31-05-2017 31-05-2017 01-05-2017 2017 5 1
1 01-06-2017 30-06-2017 30-06-2017 01-06-2017 2017 6 1
1 01-07-2017 31-07-2017 31-07-2017 01-07-2017 2017 7 1
1 01-08-2017 31-08-2017 31-08-2017 01-08-2017 2017 8 1
1 01-09-2017 30-09-2017 30-09-2017 01-09-2017 2017 9 1
1 01-10-2017 31-10-2017 31-10-2017 01-10-2017 2017 10 1
1 01-11-2017 30-11-2017 30-11-2017 01-11-2017 2017 11 1
1 01-12-2017 31-12-2017 31-12-2017 01-12-2017 2017 12 1
1 01-01-2018 31-01-2018 31-01-2018 01-01-2018 2018 1 1
1 01-02-2018 28-02-2018 28-02-2018 01-02-2018 2018 2 1
1 01-03-2018 31-03-2018 30-04-2018 01-03-2018 2018 3 1
1 01-05-2018 31-05-2018 31-10-2018 01-04-2018 2018 5 2
1 01-11-2018 30-11-2018 30-11-2018 01-06-2018 2018 11 3
1 01-12-2018 31-12-2018 NULL 01-12-2018 2018 12 3
2 01-09-2016 30-09-2016 30-09-2016 NULL 2016 9 4
2 01-10-2016 31-10-2016 31-10-2016 01-10-2016 2016 10 4
2 01-11-2016 30-11-2016 30-11-2016 01-11-2016 2016 11 4
2 01-01-2017 31-01-2017 31-01-2017 01-01-2017 2017 1 5
2 01-02-2017 28-02-2017 28-02-2017 01-02-2017 2017 2 5
2 01-03-2017 31-03-2017 31-03-2017 01-03-2017 2017 3 5
2 01-04-2017 30-04-2017 30-04-2017 01-04-2017 2017 4 5
2 01-05-2017 31-05-2017 31-05-2017 01-05-2017 2017 5 5
这是间隙和孤岛问题的变体。
我会按如下方式进行:
- 在子查询中,使用
ROW_NUMER()
按 id 和开始日期对记录进行排序,并设置一个标记来检查下一条记录的开始日期是否与当前记录的末尾连续,并且如果它有相同的 id
- 在外部查询中,对标志进行window求和;行号和标志之间的差异为您提供了分组 id
考虑:
SELECT
x.*
1 + rn - SUM(matched) OVER(ORDER BY id, rn) AS GroupingID
FROM (
SELECT
t.*
ROW_NUMBER() OVER(ORDER BY id, StartDate) rn,
CASE
WHEN
id = LEAD(id) OVER(ORDER BY id, StartDate)
AND DATEADD(day, 1, EndDate) = LEAD(StartDate) OVER(ORDER BY id, StartDate)
THEN 1
ELSE 0
END matched
FROM mytable
) x
非常感谢GBM!你已经通过指出正确的方向解决了我的问题!如果我想在两个方向上按一个 ID 对日期范围进行分组,我需要再添加一个 AND 子句 - 意思是:
SELECT
x.*
,1 + rn - SUM(matched) OVER(ORDER BY id, rn) AS GroupingID
FROM (
SELECT
t.*
,ROW_NUMBER() OVER(ORDER BY id, StartDate) rn,
,CASE
WHEN
ID = LEAD(ID) OVER(ORDER BY ID, StartDate)
AND (DATEADD(day, 1, EndDate) = LEAD(StartDate) OVER(ORDER BY ID, StartDate)
AND DATEADD(day, -1, StartDate) = LAG(EndDate) OVER(ORDER BY ID, StartDate))
THEN 1
ELSE 0
END AS Matched
FROM New_test AS t
) x
我需要一些关于基于日期范围分组的帮助,我没有任何明显的分隔符分区。我有这个数据集 (Table New_Test),其中每个 ID 都有未知数量的月+年条目,但如果它们是连接的,它们应该获得相同的分组 ID(在新列中)。例如。如果 2016 年第 9 个月之后是 2016 年第 10 个月,它们都应该获得 Grouping-ID 1。如果存在差距,即 2018 年第 3 个月到 2018 年第 5 个月的情况,则新的 Grouping ID 应该是分配。
为了找到月份的连续顺序,我绘制了 Match1 和 Match2(辅助列),它们是前一个和下一个 Start- 和 EndDate 的超前和滞后函数。
为了分配 Grouping-ID,我尝试了一个 IIF 公式,其中 StartDate = Match2 或 EndDate = Match1 之间的匹配被分配值 1 或 0。我尝试用各种替代 1 Dense_rank、排名、Row_number 的版本。如果我使用 Dense_Rank() OVER (PARTITION BY ID ORDER BY ID),我得到关于 ID 1 的三个组范围的 Grouping-ID 值 1,0,1 而不是 1,2,3 的目标因为我的数据集中没有可用的分隔符。这意味着当我稍后想按日期范围 MIN 和 MAX 对这些数据进行分组时,这两个岛屿将合并为 1 个,这不是我想要的。
我希望有人对此有一些很好的意见! :)
SELECT
ID
,StartDate
,EndDate
,LEAD(DATEADD(day,-1,StartDate),1) OVER (ORDER BY ID, Year, Month) AS Match1
,LAG(DATEADD(day,1,EndDate),1) OVER (ORDER BY ID, Year, Month) AS Match2
,IIF(StartDate= LAG(DATEADD(day,1,EndDate),1) OVER (ORDER BY ID, Year, Month)
OR EndDate =LEAD(DATEADD(day,-1,StartDate),1) OVER (ORDER BY ID, Year, Month)
,1,0) AS Grouping-ID
,Year
,Month
FROM NEW_Test
我的数据在第一次编辑后的样子:
ID StartDate EndDate Match1 Match2 Year Month
1 01-09-2016 30-09-2016 30-09-2016 NULL 2016 9
1 01-10-2016 31-10-2016 31-10-2016 01-10-2016 2016 10
1 01-11-2016 30-11-2016 30-11-2016 01-11-2016 2016 11
1 01-12-2016 31-12-2016 31-12-2016 01-12-2016 2016 12
1 01-01-2017 31-01-2017 31-01-2017 01-01-2017 2017 1
1 01-02-2017 28-02-2017 28-02-2017 01-02-2017 2017 2
1 01-03-2017 31-03-2017 31-03-2017 01-03-2017 2017 3
1 01-04-2017 30-04-2017 30-04-2017 01-04-2017 2017 4
1 01-05-2017 31-05-2017 31-05-2017 01-05-2017 2017 5
1 01-06-2017 30-06-2017 30-06-2017 01-06-2017 2017 6
1 01-07-2017 31-07-2017 31-07-2017 01-07-2017 2017 7
1 01-08-2017 31-08-2017 31-08-2017 01-08-2017 2017 8
1 01-09-2017 30-09-2017 30-09-2017 01-09-2017 2017 9
1 01-10-2017 31-10-2017 31-10-2017 01-10-2017 2017 10
1 01-11-2017 30-11-2017 30-11-2017 01-11-2017 2017 11
1 01-12-2017 31-12-2017 31-12-2017 01-12-2017 2017 12
1 01-01-2018 31-01-2018 31-01-2018 01-01-2018 2018 1
1 01-02-2018 28-02-2018 28-02-2018 01-02-2018 2018 2
1 01-03-2018 31-03-2018 30-04-2018 01-03-2018 2018 3
1 01-05-2018 31-05-2018 31-10-2018 01-04-2018 2018 5
1 01-11-2018 30-11-2018 30-11-2018 01-06-2018 2018 11
1 01-12-2018 31-12-2018 NULL 01-12-2018 2018 12
2 01-09-2016 30-09-2016 30-09-2016 NULL 2016 9
2 01-10-2016 31-10-2016 31-10-2016 01-10-2016 2016 10
2 01-11-2016 30-11-2016 30-11-2016 01-11-2016 2016 11
2 01-01-2017 31-01-2017 31-01-2017 01-01-2017 2017 1
2 01-02-2017 28-02-2017 28-02-2017 01-02-2017 2017 2
2 01-03-2017 31-03-2017 31-03-2017 01-03-2017 2017 3
2 01-04-2017 30-04-2017 30-04-2017 01-04-2017 2017 4
2 01-05-2017 31-05-2017 31-05-2017 01-05-2017 2017 5
最终结果应该是什么:
ID StartDate EndDate Match1 Match2 Year Month Grouping-ID
1 01-09-2016 30-09-2016 30-09-2016 NULL 2016 9 1
1 01-10-2016 31-10-2016 31-10-2016 01-10-2016 2016 10 1
1 01-11-2016 30-11-2016 30-11-2016 01-11-2016 2016 11 1
1 01-12-2016 31-12-2016 31-12-2016 01-12-2016 2016 12 1
1 01-01-2017 31-01-2017 31-01-2017 01-01-2017 2017 1 1
1 01-02-2017 28-02-2017 28-02-2017 01-02-2017 2017 2 1
1 01-03-2017 31-03-2017 31-03-2017 01-03-2017 2017 3 1
1 01-04-2017 30-04-2017 30-04-2017 01-04-2017 2017 4 1
1 01-05-2017 31-05-2017 31-05-2017 01-05-2017 2017 5 1
1 01-06-2017 30-06-2017 30-06-2017 01-06-2017 2017 6 1
1 01-07-2017 31-07-2017 31-07-2017 01-07-2017 2017 7 1
1 01-08-2017 31-08-2017 31-08-2017 01-08-2017 2017 8 1
1 01-09-2017 30-09-2017 30-09-2017 01-09-2017 2017 9 1
1 01-10-2017 31-10-2017 31-10-2017 01-10-2017 2017 10 1
1 01-11-2017 30-11-2017 30-11-2017 01-11-2017 2017 11 1
1 01-12-2017 31-12-2017 31-12-2017 01-12-2017 2017 12 1
1 01-01-2018 31-01-2018 31-01-2018 01-01-2018 2018 1 1
1 01-02-2018 28-02-2018 28-02-2018 01-02-2018 2018 2 1
1 01-03-2018 31-03-2018 30-04-2018 01-03-2018 2018 3 1
1 01-05-2018 31-05-2018 31-10-2018 01-04-2018 2018 5 2
1 01-11-2018 30-11-2018 30-11-2018 01-06-2018 2018 11 3
1 01-12-2018 31-12-2018 NULL 01-12-2018 2018 12 3
2 01-09-2016 30-09-2016 30-09-2016 NULL 2016 9 4
2 01-10-2016 31-10-2016 31-10-2016 01-10-2016 2016 10 4
2 01-11-2016 30-11-2016 30-11-2016 01-11-2016 2016 11 4
2 01-01-2017 31-01-2017 31-01-2017 01-01-2017 2017 1 5
2 01-02-2017 28-02-2017 28-02-2017 01-02-2017 2017 2 5
2 01-03-2017 31-03-2017 31-03-2017 01-03-2017 2017 3 5
2 01-04-2017 30-04-2017 30-04-2017 01-04-2017 2017 4 5
2 01-05-2017 31-05-2017 31-05-2017 01-05-2017 2017 5 5
这是间隙和孤岛问题的变体。
我会按如下方式进行:
- 在子查询中,使用
ROW_NUMER()
按 id 和开始日期对记录进行排序,并设置一个标记来检查下一条记录的开始日期是否与当前记录的末尾连续,并且如果它有相同的 id - 在外部查询中,对标志进行window求和;行号和标志之间的差异为您提供了分组 id
考虑:
SELECT
x.*
1 + rn - SUM(matched) OVER(ORDER BY id, rn) AS GroupingID
FROM (
SELECT
t.*
ROW_NUMBER() OVER(ORDER BY id, StartDate) rn,
CASE
WHEN
id = LEAD(id) OVER(ORDER BY id, StartDate)
AND DATEADD(day, 1, EndDate) = LEAD(StartDate) OVER(ORDER BY id, StartDate)
THEN 1
ELSE 0
END matched
FROM mytable
) x
非常感谢GBM!你已经通过指出正确的方向解决了我的问题!如果我想在两个方向上按一个 ID 对日期范围进行分组,我需要再添加一个 AND 子句 - 意思是:
SELECT
x.*
,1 + rn - SUM(matched) OVER(ORDER BY id, rn) AS GroupingID
FROM (
SELECT
t.*
,ROW_NUMBER() OVER(ORDER BY id, StartDate) rn,
,CASE
WHEN
ID = LEAD(ID) OVER(ORDER BY ID, StartDate)
AND (DATEADD(day, 1, EndDate) = LEAD(StartDate) OVER(ORDER BY ID, StartDate)
AND DATEADD(day, -1, StartDate) = LAG(EndDate) OVER(ORDER BY ID, StartDate))
THEN 1
ELSE 0
END AS Matched
FROM New_test AS t
) x