使用SQL语句按客户维度合并连续日期范围
需求说明
按客户、状态维度合并Sospensioni表中连续的日期区间:若前一个区间的结束日期与后一个区间的开始日期相邻(即后段开始日期 = 前段结束日期+1天),则将两段合并为一个区间,取区间内最早的开始日期、最晚的结束日期;不连续的区间独立保留。
测试表信息
表名:Sospensioni,原始测试数据如下:
| ClientId | Status | StartDate | EndDate |
|---|---|---|---|
| 1 | 1 | 01/01/2022 | 02/01/2022 |
| 1 | 1 | 03/01/2022 | 04/01/2022 |
| 1 | 1 | 12/01/2022 | 15/01/2022 |
| 2 | 1 | 03/01/2022 | 03/01/2022 |
| 2 | 1 | 05/01/2022 | 06/01/2022 |
预期返回结果
| ClientId | Status | StartDate | EndDate |
|---|---|---|---|
| 1 | 1 | 01/01/2022 | 04/01/2022 |
| 1 | 1 | 12/01/2022 | 15/01/2022 |
| 2 | 1 | 03/01/2022 | 03/01/2022 |
| 2 | 1 | 05/01/2022 | 06/01/2022 |
实现方案
采用经典的间断孤岛(Gaps and Islands)解法,通过窗口函数标记连续区间的分组ID,再按分组聚合即可,兼容所有支持窗口函数的主流数据库(MySQL8.0+、PostgreSQL、SQL Server等)。
核心代码(以MySQL语法为例)
WITH interval_group AS ( SELECT ClientId, Status, StartDate, EndDate, -- 遇到不连续的日期则新增分组,累计求和生成连续区间的唯一ID SUM( CASE WHEN StartDate = DATE_ADD( LAG(EndDate) OVER (PARTITION BY ClientId, Status ORDER BY StartDate), INTERVAL 1 DAY ) THEN 0 ELSE 1 END ) OVER (PARTITION BY ClientId, Status ORDER BY StartDate) AS grp_id FROM Sospensioni ) SELECT ClientId, Status, MIN(StartDate) AS StartDate, MAX(EndDate) AS EndDate FROM interval_group GROUP BY ClientId, Status, grp_id ORDER BY ClientId, StartDate;
语法适配提示:如果使用PostgreSQL,日期偏移写法替换为
LAG(EndDate) OVER (...) + INTERVAL '1 day';如果使用SQL Server,替换为DATEADD(day, 1, LAG(EndDate) OVER (...))即可,核心分组逻辑完全一致。
内容的提问来源于stack exchange,提问作者i87ce
相关产品推荐
相关产品推荐

