SQL日期筛选需求:合并dbo.OPL_Dates表中重叠日期区间
合并同一ID下重叠/连续日期区间的解决方案
搞定这个日期区间合并问题很简单,用SQL Server的窗口函数就能高效处理。先理清楚你的数据和需求:
原表数据
| ID | Start_date | End_date |
|---|---|---|
| 12345 | 1975-01-01 | 2001-12-31 |
| 12345 | 1989-01-01 | 2004-12-31 |
| 12345 | 2005-01-01 | NULL |
| 12345 | 2007-01-01 | NULL |
| 12377 | 2009-06-01 | 2009-12-31 |
| 12377 | 2013-02-07 | NULL |
| 12377 | 2010-01-01 | 2012-01-01 |
| 12489 | 2011-12-31 | NULL |
| 12489 | 2012-03-01 | 2012-04-01 |
你的需求是:对每个ID,把所有重叠或者连续的日期区间合并成一个完整区间;如果区间的End_date是NULL(表示当前仍有效),合并后也要保留NULL。
期望输出
| ID | Start_date | End_date |
|---|---|---|
| 12345 | 1975-01-01 | 2004-12-31 |
| 12345 | 2005-01-01 | NULL |
| 12377 | 2009-06-01 | 2012-01-01 |
| 12377 | 2013-02-07 | NULL |
| 12489 | 2011-12-31 | NULL |
解决方案SQL代码
WITH RankedDates AS ( SELECT ID, Start_date, End_date, -- 标记是否为新的独立区间:当前区间开始日期 > 上一个区间结束日期+1(连续也算合并) CASE WHEN LAG(ISNULL(End_date, '9999-12-31')) OVER (PARTITION BY ID ORDER BY Start_date) + 1 < Start_date THEN 1 ELSE 0 END AS IsNewInterval FROM dbo.OPL_Dates ), GroupedDates AS ( SELECT ID, Start_date, End_date, -- 累计求和生成合并区间的分组ID SUM(IsNewInterval) OVER (PARTITION BY ID ORDER BY Start_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS IntervalGroup FROM RankedDates ) SELECT ID, MIN(Start_date) AS Start_date, -- 组内只要有NULL的End_date,合并后就返回NULL,否则取最大的End_date CASE WHEN MAX(CASE WHEN End_date IS NULL THEN 1 ELSE 0 END) = 1 THEN NULL ELSE MAX(End_date) END AS End_date FROM GroupedDates GROUP BY ID, IntervalGroup ORDER BY ID, Start_date;
代码逻辑说明
RankedDates CTE:
- 先按
ID分组,每个组内按Start_date排序。 - 用
LAG函数获取上一条记录的End_date,把NULL替换成9999-12-31(远未来日期,确保NULL区间能和后续的NULL区间合并)。 - 判断当前区间的开始日期是否大于上一个区间结束日期+1,如果是,标记为新的独立区间(
IsNewInterval=1)。
- 先按
GroupedDates CTE:
- 对每个
ID,累计求和IsNewInterval,生成每个合并区间的唯一分组ID(IntervalGroup),这样属于同一个合并区间的记录会有相同的分组值。
- 对每个
最终聚合:
- 按
ID和IntervalGroup分组,取组内最小的Start_date作为合并后的开始日期。 - 处理
End_date:如果组内存在NULL(表示当前有效),合并后的End_date就保留NULL;否则取组内最大的End_date作为合并后的结束日期。
- 按
运行这段代码就能得到你想要的合并结果啦!
内容的提问来源于stack exchange,提问作者chennam
相关产品推荐
相关产品推荐

