直接用 LAG() 窗口函数最稳妥;需先按时间排序、注意时区一致性,并确保索引覆盖排序字段,否则结果可能出错且难以排查。
直接用 LAG() 窗口函数最稳妥,别硬写相关子查询。很多新手试图用 (SELECT time FROM events e2 WHERE e2.id 去找上一行,但这样在 MySQL 5.7 或 SQL Server 旧版本里会报错或极慢——因为没索引支撑的子查询在大数据量下是 O(n²)。
真正可行的做法是:先用窗口函数生成带偏移时间的临时列,再做减法。PostgreSQL、MySQL 8.0+、SQL Server 2012+ 都支持 LAG(),语法统一且可读性强。
LAG(time, 1) OVER (ORDER BY event_time) 拿上一行时间,注意必须明确 ORDER BY 字段(通常是时间戳或自增 ID)PARTITION BY user_id,否则跨用户混算LAG() 返回 NULL,减法结果也是 NULL,需用 COALESCE() 或 WHERE lagged_time IS NOT NULL 过滤不同数据库对时间相减的返回类型差异很大:PostgreSQL 返回 INTERVAL,MySQL 返回秒数(TIMESTAMPDIFF(SECOND, ...)),SQL Server 得用 DATEDIFF(second, ...)。硬写 time_col - lagged_time 在多数引擎里会报错或隐式转成天数。
TIMESTAMPDIFF(second, LAG(event_time) OVER (ORDER BY event_time), event_time)
EXTRACT(EPOCH FROM (event_time - LAG(event_time) OVER (ORDER BY event_time))) 得到秒数DATEDIFF_BIG(millisecond, LAG(event_time) OVER (ORDER BY event_time), event_time)(DATEDIFF 有溢出风险)DATE_SUB/NOW() 类函数模拟,精度丢失且难维护常见原因是时间字段含毫秒但排序未精确到毫秒级,或者存在重复时间戳。比如两条事件都发生在 '2024-01-01 10:00:00',ORDER BY event_time 无法保证稳定顺序,LAG() 可能取到“错误的上一条”。
ORDER BY 中加入唯一字段兜底,例如 ORDER BY event_time, id
TIMESTAMP(带时区)或 DATETIME(无时区),混用会导致跨时区计算偏差即使用了 LAG(),如果没索引,全表扫描照样慢。窗口函数本身不自动走索引,它依赖 ORDER BY 字段是否有有效索引。
ORDER BY 的字段建联合索引,例如 CREATE INDEX idx_events_time_id ON events(event_time, id)
WHERE event_time > NOW() - INTERVAL '7 days' 先过滤,别让窗口函数扫全表时间间隔计算看着简单,但实际卡点都在排序稳定性、时区处理和索引覆盖上。漏掉任意一个,结果就可能错得离谱,而且很难一眼发现。