SQL如何基于0和1切换的ping_result状态列分组取每组首条记录
需求说明
现有数据表包含date_time、id、name、ip、ping_result字段,其中ping_result取值为0或1,会随时间发生状态切换。
需求:针对指定id(示例为id=1的Mario),每次ping_result发生状态变更时,获取该状态序列的第一条记录。
原有代码问题
原有实现直接按ping_result分区生成行号,只会将所有ping_result=0和ping_result=1的记录各自分为一组,无法实现状态切换时重置分区计数的效果,因此只能拿到全局最新的0和1状态记录,无法匹配每次状态切换的需求。
原有代码如下:
WITH cte AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY ping_result ORDER BY [date_time] DESC) AS row_num FROM [table] where id = 1 ) select * from cte WHERE row_num = 1
正确实现方案
通过相邻行状态对比+分组累加标记的方法即可实现需求,兼容所有支持窗口函数的数据库(MySQL 8.0+、PostgreSQL、SQL Server等),实现代码如下:
WITH step1 AS ( -- 第一步:获取上一行的ping_result,用于对比状态是否变化 SELECT *, LAG(ping_result) OVER (ORDER BY date_time ASC) AS prev_ping FROM [table] WHERE id = 1 ), step2 AS ( -- 第二步:状态变化时打标记1,初始第一条记录也标记为1 SELECT *, CASE WHEN ping_result != prev_ping OR prev_ping IS NULL THEN 1 ELSE 0 END AS is_change FROM step1 ), step3 AS ( -- 第三步:累加标记生成连续状态分组ID,同一连续状态的group_id相同 SELECT *, SUM(is_change) OVER (ORDER BY date_time ASC) AS group_id FROM step2 ), step4 AS ( -- 第四步:每个分组内按时间排序,取第一条即为状态切换后的首条记录 SELECT *, ROW_NUMBER() OVER (PARTITION BY group_id ORDER BY date_time ASC) AS row_num FROM step3 ) SELECT date_time, id, name, ip, ping_result FROM step4 WHERE row_num = 1 ORDER BY date_time ASC;
输出示例
和需求预期一致,返回每次状态切换后的第一条记录:
| date_time | id | name | ip | ping_result |
|---|---|---|---|---|
| 2021-08-26 14:00 | 1 | Mario | 1.1.1.1 | 1 |
| 2021-08-26 14:10 | 1 | Mario | 1.1.1.1 | 0 |
| 2021-08-26 14:20 | 1 | Mario | 1.1.1.1 | 1 |
内容的提问来源于stack exchange,提问作者Ocean Airdrop
相关产品推荐
相关产品推荐

