LAG()必须配合ORDER BY才能可靠排序,否则行序不确定;按用户分组需加PARTITION BY user_id;时间差计算应使用数据库特有函数如EXTRACT或TIMESTAMPDIFF;需用COALESCE等处理NULL值;ORDER BY字段须建索引以防性能下降。

LAG() 必须配合 ORDER BY 才能正确排序
直接在 SELECT 中写 LAG(order_time) 但没指定排序逻辑,结果完全不可靠——LAG() 不会自动按时间排序,它只按查询实际返回的行序取上一行,而无 ORDER BY 时行序是数据库引擎决定的,可能每次都不一样。
正确做法是在窗口定义中显式用 ORDER BY order_time:
SELECT order_id, order_time, LAG(order_time) OVER (ORDER BY order_time) AS prev_order_time FROM orders;
如果按用户分组计算每个用户的前后间隔,必须加 PARTITION BY user_id,否则会把所有用户混在一起排:
-
OVER (PARTITION BY user_id ORDER BY order_time)→ 每个用户独立计算 -
OVER (ORDER BY order_time)→ 全表统一排序,跨用户错乱
计算时间差要用具体类型减法,不能直接减 timestamp
不同数据库对 timestamp 相减行为不一致:PostgreSQL 支持直接相减得 interval,MySQL 会报错或返回整数秒,SQL Server 要用 DATEDIFF()。最通用安全的做法是用函数显式提取单位:
SELECT order_id, order_time, LAG(order_time) OVER (PARTITION BY user_id ORDER BY order_time) AS prev_time, EXTRACT(EPOCH FROM (order_time - LAG(order_time) OVER (PARTITION BY user_id ORDER BY order_time))) AS diff_seconds -- PostgreSQL
MySQL 用户得换成:
TIMESTAMPDIFF(SECOND, LAG(order_time) OVER ..., order_time)- 注意
TIMESTAMPDIFF参数顺序是(unit, start, end),别把前后颠倒
NULL 值处理不当会导致间隔计算中断
首条订单的 LAG() 结果一定是 NULL,如果直接参与减法(如 order_time - LAG(...)),整行结果变 NULL,后续 CASE WHEN 或聚合容易漏数据。
建议统一用 COALESCE() 或条件过滤:
- 跳过首单:
WHERE LAG(order_time) OVER (...) IS NOT NULL - 补默认值:
COALESCE(order_time - LAG(order_time) OVER (...), INTERVAL '0 second') - 标记缺失:
CASE WHEN LAG(order_time) OVER (...) IS NULL THEN 'first_order' ELSE ... END
别依赖 WHERE 后再套窗口函数——窗口计算在 WHERE 之前执行,必须在窗口内或外层处理 NULL。
性能陷阱:ORDER BY 字段没索引会拖慢大表查询
LAG() 的窗口排序不是免费的。如果 ORDER BY order_time 对应字段没索引,100 万行订单表可能触发全表排序,执行时间从毫秒级涨到秒级。
检查执行计划里是否有 Sort 节点;优化方式只有两个:
- 确保
order_time(或(user_id, order_time))有 B-tree 索引 - 避免在子查询里嵌套多层窗口函数,先用 CTE 提前算好
LAG(),再 join 或过滤
复合分区场景下,PARTITION BY user_id ORDER BY order_time 最好对应联合索引,否则排序无法利用索引下推。

















