LEAD()仅需一次排序和线性遍历,时间复杂度O(n log n),而自连接每行重查导致O(n²)性能劣化;它不依赖物理ID连续性,支持默认值防NULL,且强制要求ORDER BY保证逻辑顺序稳定。
自连接查“下一行”时,典型写法是 JOIN t a ON a.id = b.id + 1 或 JOIN t a ON a.time = b.time + INTERVAL '1 day'。问题在于:数据库对主表每一行 b 都要重新执行一次关联逻辑,匹配条件不走索引时就是全表扫描——10 万行主表,就可能扫 10 万 × 10 万 行。
LEAD() 完全不同:它只做一次排序(按 ORDER BY 字段),然后线性遍历一遍数据,在内存缓冲区里直接取相邻行值。时间复杂度从 O(n²) 降到 O(n log n),数据量越大,差距越明显。
EXPLAIN 显示 Type: ALL、Rows 列数值爆炸、出现 DEPENDENT SUBQUERY
LEAD() 的执行计划里通常只有 Using filesort(仅当 ORDER BY 字段无索引时)或直接 Index Scan
WHERE 过滤再开窗口(比如只查最近 30 天),能大幅减少排序开销生产环境的 id 绝对不连续:删过记录、批量导入、分库分表、UUID 主键……任何一种都会让 a.id = b.id + 1 返回空或错配。
LEAD(value) OVER (PARTITION BY user_id ORDER BY event_time, id) 只认 ORDER BY 定义的逻辑顺序,和存储物理位置无关。只要排序字段稳定(比如加 id 作二级排序),结果就可复现。
LEAD(value) OVER (ORDER BY event_time) —— event_time 重复时,MySQL 8.0 每次返回的“下一行”可能不同ORDER BY event_time, id 或 ORDER BY event_time, created_at
event_time 含 NULL,默认排最前,首行 LEAD() 拿不到值;可用 IFNULL(event_time, '1970-01-01') 预处理自连接遇到“没有下一行”时,只能靠 LEFT JOIN 返回 NULL,后续计算(如差值、增长率)直接变 NULL,还得额外 COALESCE() 处理。
LEAD(value, 1, 0) 第三个参数就是默认值:最后一行调用时直接返回 0,不用判空。
NULLIF(amount, 0),分子用 LEAD(amount, 1, 0),整个表达式不会崩LEAD(value, 1) DEFAULT 0 这种语法,必须写成 LEAD(value, 1, 0)
LEAD() 本身不支持表达式作为默认值LEAD() 必须配合 ORDER BY,否则 MySQL 报错:ERROR 3589 (HY000): Window '<unnamed>' requires an ORDER BY clause</unnamed>。这不是可选项,是语法强制。
LEAD(amount) OVER (PARTITION BY region) → 报错LEAD(amount) OVER (PARTITION BY region ORDER BY ym)
ym 是字符串(如 '2024-01'),确保字典序等于时间序;否则改用 STR_TO_DATE(ym, '%Y-%m') 转日期再排序PARTITION BY 可省略,但一旦用了,ORDER BY 仍不可少;没分区时,ORDER BY 就是对全表排序真正卡住性能的从来不是函数名本身,而是排序字段有没有索引、ORDER BY 是否稳定、以及你有没有在 WHERE 阶段就砍掉无效数据。窗口函数快,但不是免死金牌。