自连接识别时间段重叠的原理是:对每条记录t1,通过on t2.start_time
用自连接识别时间段重叠的原理是什么
两个事件时间窗口重叠,当且仅当:
t1.start_time t2.start_time
这是判断区间相交最简且无遗漏的条件(开闭无关,只要统一按左闭右开或含端点处理即可)。
注意不能只写t1.start_time BETWEEN t2.start_time AND t2.end_time,它漏掉t1完全包住t2的情况。实际统计并发量时,我们固定一个“锚点时刻”(通常是每个事件的
start_time),然后对每个时刻,数出所有覆盖它的事件数。自连接就是让每条记录作为锚点,和全表比对哪些其他记录与之重叠。如何写出自连接 + 聚合的并发峰值查询
目标是:对每个原始事件,算出“该事件开始时刻有多少并发活动”,再取最大值。
SELECT MAX(concurrent_cnt) AS peak_concurrency FROM ( SELECT COUNT(*) AS concurrent_cnt FROM events t1 INNER JOIN events t2 ON t2.start_time t1.start_time GROUP BY t1.id, t1.start_time ) t;关键点:
GROUP BY t1.id, t1.start_time确保每个事件独立计数,避免因多条相同时间的记录被错误合并- 若只需峰值(不要求对应时刻),
MAX()够用;若还要知道发生在哪一刻,外层需保留t1.start_time并用ORDER BY concurrent_cnt DESC LIMIT 1- 索引建议:在
(start_time, end_time)上建联合索引,能加速连接条件中的范围比较为什么直接用窗口函数行不通
窗口函数如
OVER (ORDER BY start_time ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)只能做累积计数,无法剔除已结束的事件。它会把所有已开始但未指定“是否仍运行”的事件全算进来,结果偏高。常见误写:
-- ❌ 错误:这只是累计启动数,不是并发数 COUNT(*) OVER (ORDER BY start_time RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)真正需要的是“当前时刻仍活跃的事件数”,必须显式判断
end_time > 当前时刻—— 这正是自连接中t2.end_time > t1.start_time所承担的角色。大数据量下怎么避免 O(n²) 性能崩盘
自连接在百万级记录上可能慢到不可接受。优化路径有两条:
- 改用“事件点扫描法”:把每个
start_time记为 +1,end_time记为 -1,ORDER BY time, type后累加,过程中记录最大值。SQL 实现依赖UNION ALL和窗口函数,但复杂度降到 O(n log n)- 如果数据库支持(如 PostgreSQL 14+、ClickHouse),可用
tsrange类型 +GIST索引 +OVERLAPS操作符,语法更简洁,性能也更好- 务必给
start_time和end_time单独加索引,即使建了联合索引,某些执行计划仍可能只用到前导列重叠判断看着简单,但端点是否包含、时区是否对齐、NULL 值如何处理——这些细节一旦错,统计结果就全偏了。别跳过
IS NOT NULL过滤和COALESCE(end_time, NOW())补缺逻辑。











