如何在SQL中按多列对连续行进行分组
按多列对连续行分组的SQL解决方案
首先先把你提供的原始数据整理成更清晰的表格形式:
原始数据
| Date | User | Time | Location | Service | Count |
|---|---|---|---|---|---|
| 1/1/2018 | Nick | 12:00 | Location A | X | 1 |
| 1/1/2018 | Nick | 12:01 | Location A | Y | 1 |
| 1/1/2018 | John | 12:02 | Location B | Z | 1 |
| 1/1/2018 | Harry | 12:03 | Location A | X | 1 |
| 1/1/2018 | Harry | 12:04 | Location A | X | 1 |
| 1/1/2018 | Harry | 12:05 | Location B | Y | 1 |
| 1/1/2018 | Harry | 12:06 | Location B | X | 1 |
| 1/1/2018 | Nick | 12:07 | Location A | X | 1 |
| 1/1/2018 | Nick | 12:08 | Location A | Y | 1 |
你要解决的是将连续行中User、Location、Service完全相同的行归为一组,并统计每组的总次数(或其他聚合指标),这是SQL里很常见的“连续相同属性分组”问题,核心思路是用窗口函数来标记分组边界,具体步骤如下:
步骤1:标记分组边界
首先用LAG()窗口函数对比当前行和上一行的User、Location、Service三个字段,只要其中一个字段不同,就标记为一个新分组的起点(用1表示),否则标记为0:
SELECT *, CASE -- 对比上一行的三个字段,只要有一个不同,就标记为新分组 WHEN LAG(User) OVER (ORDER BY Date, Time) != User OR LAG(Location) OVER (ORDER BY Date, Time) != Location OR LAG(Service) OVER (ORDER BY Date, Time) != Service THEN 1 ELSE 0 END AS group_flag FROM your_table_name ORDER BY Date, Time;
这里用ORDER BY Date, Time是为了保证跨天的情况下排序依然正确,如果你确定数据都是同一天,可以只按Time排序。
步骤2:生成唯一分组ID
通过累积求和group_flag,我们可以得到每个连续组的唯一ID——每遇到一个标记为1的行,分组ID就会递增,这样相同连续组的行就会拥有同一个ID:
SELECT *, -- 累积求和生成分组ID SUM(group_flag) OVER (ORDER BY Date, Time ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS group_id FROM ( -- 嵌套第一步的查询 SELECT *, CASE WHEN LAG(User) OVER (ORDER BY Date, Time) != User OR LAG(Location) OVER (ORDER BY Date, Time) != Location OR LAG(Service) OVER (ORDER BY Date, Time) != Service THEN 1 ELSE 0 END AS group_flag FROM your_table_name ) t1 ORDER BY Date, Time;
步骤3:按分组ID聚合统计
最后用group_id加上User、Location、Service分组,就能得到每个连续组的统计结果了:
SELECT MIN(Date) AS date, User, Location, Service, MIN(Time) AS start_time, -- 组内最早时间 MAX(Time) AS end_time, -- 组内最晚时间 SUM(Count) AS total_count -- 组内总次数 FROM ( SELECT *, SUM(group_flag) OVER (ORDER BY Date, Time ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS group_id FROM ( SELECT *, CASE WHEN LAG(User) OVER (ORDER BY Date, Time) != User OR LAG(Location) OVER (ORDER BY Date, Time) != Location OR LAG(Service) OVER (ORDER BY Date, Time) != Service THEN 1 ELSE 0 END AS group_flag FROM your_table_name ) t1 ) t2 GROUP BY group_id, User, Location, Service ORDER BY start_time;
最终结果示例
执行上面的SQL后,你会得到类似这样的结果:
| date | User | Location | Service | start_time | end_time | total_count |
|---|---|---|---|---|---|---|
| 1/1/2018 | Nick | Location A | X | 12:00 | 12:00 | 1 |
| 1/1/2018 | Nick | Location A | Y | 12:01 | 12:01 | 1 |
| 1/1/2018 | John | Location B | Z | 12:02 | 12:02 | 1 |
| 1/1/2018 | Harry | Location A | X | 12:03 | 12:04 | 2 |
| 1/1/2018 | Harry | Location B | Y | 12:05 | 12:05 | 1 |
| 1/1/2018 | Harry | Location B | X | 12:06 | 12:06 | 1 |
| 1/1/2018 | Nick | Location A | X | 12:07 | 12:07 | 1 |
| 1/1/2018 | Nick | Location A | Y | 12:08 | 12:08 | 1 |
这样就完美实现了按多列对连续行分组统计的需求啦!
内容的提问来源于stack exchange,提问作者Niko
相关产品推荐
相关产品推荐

