SQL分组时如何按时间序列拆分同值连续子组获取各子组首条记录
解决方案
这是典型的序列孤岛问题,可通过新增辅助分组列实现需求,核心逻辑是为每一段连续的相同A、B记录生成唯一的组标识,再按组标识+A、B分组取最小日期即可。
方案1:双窗口函数差值法(通用性最强)
实现逻辑
- 按C字段升序为所有记录生成全局连续行号
rn_all - 按A、B字段分组,组内按C升序生成组内行号
rn_ab - 生成辅助列
group_id = rn_all - rn_ab:连续的相同A、B记录的group_id值固定,一旦中间插入其他A、B记录,后续相同A、B的group_id会发生变化,以此区分不连续的同值分段 - 按
A、B、group_id分组,取每组C的最小值即为目标结果
参考SQL(支持窗口函数的SQL引擎均可运行,如MySQL8.0+、Spark SQL、Hive、PostgreSQL等)
WITH t1 AS ( -- 生成两个行号 SELECT A, B, C, ROW_NUMBER() OVER(ORDER BY C) AS rn_all, ROW_NUMBER() OVER(PARTITION BY A, B ORDER BY C) AS rn_ab FROM 你的表名 ), t2 AS ( -- 生成组标识 SELECT A, B, C, rn_all - rn_ab AS group_id FROM t1 ) -- 分组取最小日期 SELECT A, B, MIN(C) AS C FROM t2 GROUP BY A, B, group_id ORDER BY C;
方案2:lag标记累加法
实现逻辑
- 用
LAG窗口函数取上一行的A、B值,和当前行对比,若不同则标记为1,相同标记为0 - 对标记值按C升序累加,得到的累加值就是连续段的唯一组标识
- 按组标识分组取最小C即可
参考SQL
WITH t1 AS ( SELECT A, B, C, -- 判断当前行和上一行是否为相同A、B CASE WHEN LAG(A, 1, -1) OVER(ORDER BY C) = A AND LAG(B, 1, '') OVER(ORDER BY C) = B THEN 0 ELSE 1 END AS flag FROM 你的表名 ), t2 AS ( -- 累加标记得到组ID SELECT A, B, C, SUM(flag) OVER(ORDER BY C ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS group_id FROM t1 ) SELECT A, B, MIN(C) AS C FROM t2 GROUP BY group_id, A, B ORDER BY C;
两种方案均可得到你给出的期望输出结果,第一种方案逻辑更简洁不易出错,推荐优先使用。
内容的提问来源于stack exchange,提问作者Arthur Clerc-Gherardi
相关产品推荐
相关产品推荐

