如何用窗口函数实现会话分组标记及数据聚合(非T-SQL)
会话分组统计与窗口函数实现方案
给定原始数据集:
| 用户名 | 事件时间 | 是否新会话 |
|---|---|---|
| userA | 2022-09-30 00:00:01.000000 | True |
| userA | 2022-09-30 00:00:02.000000 | False |
| userA | 2022-09-30 01:00:00.000000 | True |
| userA | 2022-09-30 02:00:00.000000 | True |
| userA | 2022-09-30 02:00:02.000000 | False |
| userA | 2022-09-30 02:00:04.000000 | False |
| userA | 2022-09-30 00:00:05.000000 | False |
| userB | 2022-09-30 03:00:00.000000 | True |
需要先为每组连续记录(直到下一条是否新会话=True的记录)添加相同的分组标记值,再通过聚合得到如下会话统计结果(注:原示例中userA第一个会话的末次事件时间和事件数有误,已修正为按时间排序后的正确结果):
| 会话ID | 用户名 | 首次事件时间 | 末次事件时间 | 事件数 |
|---|---|---|---|---|
| 1 | userA | 2022-09-30 00:00:01.000000 | 2022-09-30 00:00:05.000000 | 3 |
| 2 | userA | 2022-09-30 01:00:00.000000 | 2022-09-30 01:00:00.000000 | 1 |
| 3 | userA | 2022-09-30 02:00:00.000000 | 2022-09-30 02:00:04.000000 | 3 |
| 4 | userB | 2022-09-30 03:00:00.000000 | 2022-09-30 03:00:00.000000 | 1 |
以下是具体实现方案:
一、用窗口函数实现分组标记
核心逻辑是对是否新会话的起点做累计计数,从而为同一会话的记录分配相同的分组ID。
1. 使用SUM()窗口函数
将布尔值是否新会话转为数值(True=1,False=0),按用户名分区、事件时间排序后做累计求和:
SELECT 用户名, 事件时间, 是否新会话, SUM(CASE WHEN 是否新会话 THEN 1 ELSE 0 END) OVER (PARTITION BY 用户名 ORDER BY 事件时间) AS 会话分组ID FROM 原始表 ORDER BY 用户名, 事件时间;
执行后,每个会话内的所有记录会被分配相同的会话分组ID,后续只需按用户名和会话分组ID聚合即可得到统计结果。
2. 使用COUNT()窗口函数
通过COUNT()仅统计是否新会话=True的记录,按用户名分区、事件时间排序累计计数,同样能生成会话分组ID:
SELECT 用户名, 事件时间, 是否新会话, COUNT(CASE WHEN 是否新会话 THEN 1 END) OVER (PARTITION BY 用户名 ORDER BY 事件时间) AS 会话分组ID FROM 原始表 ORDER BY 用户名, 事件时间;
该方法与SUM()逻辑本质一致,都是对新会话起点做累计计数。
二、直接从原始数据生成最终统计结果的方案
可以将分组标记与聚合步骤合并,无需单独生成中间表:
SELECT ROW_NUMBER() OVER (ORDER BY MIN(事件时间)) AS 会话ID, 用户名, MIN(事件时间) AS 首次事件时间, MAX(事件时间) AS 末次事件时间, COUNT(*) AS 事件数 FROM ( SELECT *, SUM(CASE WHEN 是否新会话 THEN 1 ELSE 0 END) OVER (PARTITION BY 用户名 ORDER BY 事件时间) AS 会话分组ID FROM 原始表 ) t GROUP BY 用户名, 会话分组ID ORDER BY 会话ID;
也可以用CTE(公共表表达式)优化可读性:
WITH 会话分组 AS ( SELECT 用户名, 事件时间, SUM(CASE WHEN 是否新会话 THEN 1 ELSE 0 END) OVER (PARTITION BY 用户名 ORDER BY 事件时间) AS 会话分组ID FROM 原始表 ) SELECT ROW_NUMBER() OVER (ORDER BY MIN(事件时间)) AS 会话ID, 用户名, MIN(事件时间) AS 首次事件时间, MAX(事件时间) AS 末次事件时间, COUNT(*) AS 事件数 FROM 会话分组 GROUP BY 用户名, 会话分组ID ORDER BY 会话ID;
以上两种方式都能直接从原始表得到最终的会话统计结果,省去中间表处理步骤。
内容的提问来源于stack exchange,提问作者1131
相关产品推荐
相关产品推荐

