正确写法需加partition by user_id和order by user_id, order_time;null要where过滤;时间差按数据库选datediff、timestampdiff或extract;大表需联合索引优化。

LEAD 函数怎么写才能拿到「下一次下单时间」
直接用 LEAD(order_time) 是错的——它默认按当前查询结果的物理顺序取下一行,而订单表通常没按用户+时间排序。必须显式加 ORDER BY user_id, order_time,且 PARTITION BY user_id 防止跨用户错拉。漏掉这两点,LEAD 会把张三的第二单和李四的第一单配对,差值全乱。
正确写法示例:
SELECT user_id, order_time, LEAD(order_time) OVER (PARTITION BY user_id ORDER BY order_time) AS next_order_time FROM orders;
计算间隔时 NULL 和边界值怎么处理
每个用户的最后一笔订单,LEAD 必然返回 NULL;如果某用户只有一笔订单,全部 next_order_time 都是 NULL。直接 AVG(DATEDIFF(next_order_time, order_time)) 会导致整行被排除(MySQL/PostgreSQL 中 AVG 自动忽略 NULL),但逻辑上「单笔订单无间隔」不应参与平均——这点容易被忽略。
建议做法:
- 用
WHERE next_order_time IS NOT NULL显式过滤掉末尾无效行 - 对每个用户先算出所有有效间隔(单位:秒/天),再
AVG,避免用全局AVG混淆不同用户样本量 - 若需保留单订单用户,可设间隔为
NULL或0,但需在业务层明确含义
日期差用 DATEDIFF 还是直接减?各数据库差异在哪
DATEDIFF 行为不统一:MySQL 的 DATEDIFF(a, b) 返回 a−b 的**天数整数**;PostgreSQL 没这个函数,得用 a - b(返回 interval);SQL Server 的 DATEDIFF(day, b, a) 也只取天数,会截断时间部分。
关键影响:
- 若订单精确到秒,用天数会丢失精度(比如间隔 23 小时算成 0 天)
- 想算小时级平均,MySQL 得改用
TIMESTAMPDIFF(HOUR, order_time, next_order_time) - PostgreSQL 推荐
EXTRACT(EPOCH FROM (next_order_time - order_time)) / 3600转小时
性能隐患:大表上 LEAD + GROUP BY 容易慢
当订单表超千万行,PARTITION BY user_id ORDER BY order_time 会触发大量 sort 操作。即使有 (user_id, order_time) 联合索引,窗口函数仍可能无法完全避免临时文件。
提速建议:
- 确认索引存在:
CREATE INDEX idx_user_time ON orders(user_id, order_time); - 避免在子查询里嵌套多层窗口函数,先用 CTE 提取
next_order_time,再外层算差值和平均 - 如果只要整体均值(非每人一个值),可在应用层分页拉取用户数据,用代码聚合,减轻数据库压力
真正难的不是写出 LEAD,而是确认每一步的 NULL 边界、时间精度取舍、以及千万级数据下的执行计划是否真的走了索引。










