PostgreSQL中每小时重叠并发会话统计方案求助
计算每小时并发会话数的解决方案
这问题我之前处理类似需求时也碰到过,要算出每个小时的并发活跃会话,核心是判断每个会话的活跃时间段和目标小时是否有重叠。咱们一步步来拆解:
先明确逻辑
一个会话会被计入某个小时的并发数,当且仅当:
- 它的登录时间 不晚于 该小时的结束时刻(比如10:00小时的结束是10:59:59)
- 它的登出时间 不早于 该小时的开始时刻(比如10:00小时的开始是10:00:00)
如果会话还没登出(只有login记录),那默认它在当前及之后的小时都是活跃的。
解决方案(以PostgreSQL为例)
我们可以用CTE(公共表表达式)来分步处理:
WITH session_times AS ( -- 先把每个会话的登录、登出时间聚合到一行 SELECT session_id, MAX(CASE WHEN action = 'login' THEN timestamp END) AS login_time, MAX(CASE WHEN action = 'logout' THEN timestamp END) AS logout_time FROM sessions GROUP BY session_id ), hourly_intervals AS ( -- 生成需要统计的所有小时区间(从最早登录时间的整点到最晚登出时间的整点) SELECT generate_series( DATE_TRUNC('hour', MIN(login_time)), DATE_TRUNC('hour', MAX(logout_time)), INTERVAL '1 hour' ) AS hour_start ) -- 统计每个小时的并发数 SELECT TO_CHAR(h.hour_start, 'YYYY-MM-DD HH24:00') AS timestamp, COUNT(st.session_id) AS concurrent_sessions FROM hourly_intervals h LEFT JOIN session_times st -- 判断会话活跃时间段和当前小时是否重叠 ON st.login_time <= h.hour_start + INTERVAL '59 minutes 59 seconds' AND COALESCE(st.logout_time, CURRENT_TIMESTAMP) >= h.hour_start GROUP BY h.hour_start ORDER BY h.hour_start;
代码解释
- session_times:把每个会话的login和logout时间合并到一行,避免后续重复处理同一会话的多条记录。
- hourly_intervals:用
generate_series自动生成所有需要统计的小时点,不用手动写时间范围。 - 最后关联两个表,通过时间重叠条件筛选出每个小时的活跃会话,统计数量即可。
MySQL版本适配
如果用MySQL(没有generate_series),可以用递归CTE生成小时序列:
WITH RECURSIVE session_times AS ( SELECT session_id, MAX(CASE WHEN action = 'login' THEN timestamp END) AS login_time, MAX(CASE WHEN action = 'logout' THEN timestamp END) AS logout_time FROM sessions GROUP BY session_id ), hourly_intervals AS ( -- 初始小时:最早登录时间的整点 SELECT DATE_FORMAT(MIN(login_time), '%Y-%m-%d %H:00:00') AS hour_start FROM session_times UNION ALL -- 递归生成后续每小时 SELECT DATE_ADD(hour_start, INTERVAL 1 HOUR) FROM hourly_intervals WHERE hour_start < (SELECT DATE_FORMAT(MAX(logout_time), '%Y-%m-%d %H:00:00') FROM session_times) ) SELECT hour_start AS timestamp, COUNT(st.session_id) AS concurrent_sessions FROM hourly_intervals h LEFT JOIN session_times st ON st.login_time <= DATE_ADD(hour_start, INTERVAL 59 MINUTE 59 SECOND) AND (st.logout_time >= h.hour_start OR st.logout_time IS NULL) GROUP BY h.hour_start ORDER BY h.hour_start;
验证你的示例数据
用你给出的测试数据跑上面的代码,会得到和你预期完全一致的结果:
- 10:00小时有2个活跃会话(session1、session2)
- 11:00小时有3个(session2、session3、session4)
- 以此类推,完全匹配你要的输出。
内容的提问来源于stack exchange,提问作者josip
相关产品推荐
相关产品推荐

