基于Present/Absent列合并重复记录,取Min Start_Date和Max End_Date
解决方案:按ID+Present/Absent组合合并记录
看起来你需要把同一ID下,Present/Absent组合相同的记录合并成一条主记录,并且取该组的最小开始日期和最大结束日期。根据你的描述,我分两种常见场景给你提供可行的SQL方案:
场景1:所有相同组合的记录合并(不管时间是否连续)
如果只要ID+Present/Absent组合相同,不管这些记录在时间线上是否连续,都要合并成一条,那直接用基础的分组聚合就可以解决,不需要窗口函数:
SELECT id, Present, Absent, MIN(Start_Date) AS Start_Date, MAX(End_Date) AS End_Date FROM your_table GROUP BY id, Present, Absent ORDER BY id, Start_Date;
逻辑说明:
- 用
GROUP BY id, Present, Absent把所有相同ID、相同Present/Absent组合的记录归为一组 - 用
MIN(Start_Date)取该组最早的开始时间,MAX(End_Date)取该组最晚的结束时间 - 最后按ID和合并后的开始日期排序,和你之前的排序逻辑一致
场景2:连续的相同组合记录合并(间隙与岛屿问题)
如果你需要的是同一ID下按Start_Date排序后,连续的相同Present/Absent组合记录才合并(比如中间插了其他组合的记录,就不合并),这时候需要用窗口函数处理“间隙与岛屿”问题,步骤如下:
-- 步骤1:标记每行与上一行的组合是否发生变化 WITH numbered_rows AS ( SELECT *, CASE -- 对比当前行与上一行的Present/Absent组合 WHEN LAG(CONCAT(Present, '|', Absent)) OVER (PARTITION BY id ORDER BY Start_Date) != CONCAT(Present, '|', Absent) THEN 1 -- 组合变化,标记为新组的开始 ELSE 0 -- 组合不变,属于同一组 END AS group_change FROM your_table ), -- 步骤2:生成每个连续组的唯一ID grouped_rows AS ( SELECT *, -- 累计求和,每次遇到group_change=1时,组ID递增 SUM(group_change) OVER (PARTITION BY id ORDER BY Start_Date) AS group_id FROM numbered_rows ) -- 步骤3:按ID和组ID聚合,生成主记录 SELECT id, Present, Absent, MIN(Start_Date) AS Start_Date, MAX(End_Date) AS End_Date FROM grouped_rows GROUP BY id, group_id, Present, Absent ORDER BY id, Start_Date;
逻辑说明:
- 标记组变化:用
LAG()窗口函数获取当前行的上一行记录的Present/Absent组合,和当前行对比,如果不同就标记为1(表示新组开始) - 生成组ID:用
SUM() OVER()累计求和,这样连续的相同组合记录会得到同一个group_id,不同组合的记录会得到新的ID - 聚合生成主记录:最后按
id和group_id分组,取每组的最小开始日期和最大结束日期
注意事项:
- 不同数据库的窗口函数语法基本一致,但如果是MySQL 5.x版本,不支持
LAG()和CTE(WITH语句),需要改用变量来实现组ID的生成 - 如果Present/Absent是布尔值或可以直接比较的类型,也可以不用
CONCAT(),直接写LAG(Present) OVER (...) != Present OR LAG(Absent) OVER (...) != Absent来判断组合变化
内容的提问来源于stack exchange,提问作者Salva
相关产品推荐
相关产品推荐

