如何不使用循环合并并调整重叠日期范围?
无循环实现重叠日期记录合并方案
可以通过SQL的**递归CTE(公共表表达式)**实现,无需显式循环。以下是针对需求的具体实现方案:
核心思路
- 对每个
Id下的记录按FromDate排序,计算每条记录的日期区间天数(EndDate - FromDate)。 - 通过递归CTE逐步合并重叠记录:
- 以每个
Id的最早记录为初始合并组; - 依次处理后续记录,若当前记录的
FromDate处于当前合并组的累计日期区间内,则将该记录的天数累加到组的EndDate;若不重叠,则新建一个合并组。
- 以每个
- 最后按合并组聚合,得到最终结果。
代码示例(以SQL Server为例)
WITH ranked_data AS ( -- 给每个Id的记录排序,计算每条记录的区间天数 SELECT Id, FromDate, EndDate, DATEDIFF(day, FromDate, EndDate) AS days, ROW_NUMBER() OVER (PARTITION BY Id ORDER BY FromDate) AS rn FROM your_table ), recursive_merge AS ( -- 锚点:每个Id的第一条记录作为初始合并组 SELECT Id, FromDate, EndDate AS current_end, rn, 1 AS group_id FROM ranked_data WHERE rn = 1 UNION ALL -- 递归处理后续记录,判断是否合并到当前组 SELECT rd.Id, -- 重叠则沿用主记录的FromDate,否则用当前记录的FromDate CASE WHEN rd.FromDate <= rm.current_end THEN rm.FromDate ELSE rd.FromDate END, -- 重叠则累加天数到当前组的EndDate,否则用当前记录的EndDate CASE WHEN rd.FromDate <= rm.current_end THEN DATEADD(day, rd.days, rm.current_end) ELSE rd.EndDate END, rd.rn, -- 重叠则保持组ID,否则新建组 CASE WHEN rd.FromDate <= rm.current_end THEN rm.group_id ELSE rm.group_id + 1 END FROM recursive_merge rm JOIN ranked_data rd ON rm.Id = rd.Id AND rd.rn = rm.rn + 1 ) -- 按Id和合并组聚合,取最终结果 SELECT Id, MIN(FromDate) AS FromDate, MAX(current_end) AS EndDate FROM recursive_merge GROUP BY Id, group_id ORDER BY Id, FromDate;
适配不同SQL方言说明
- MySQL:
DATEDIFF函数参数顺序为DATEDIFF(EndDate, FromDate),DATEADD替换为DATE_ADD(current_end, INTERVAL rd.days DAY)。 - PostgreSQL:用
(EndDate - FromDate)::int计算天数,DATEADD替换为current_end + rd.days * INTERVAL '1 day'。
结果验证
针对你提供的示例数据,执行后将得到:
| Id | FromDate | EndDate |
|---|---|---|
| 1 | 2024-01-01 | 2024-01-16 |
| 1 | 2024-02-05 | 2024-02-10 |
完全符合需求中的合并规则。
内容的提问来源于stack exchange,提问作者Tua Lee
相关产品推荐
相关产品推荐

