lag()用于回溯历史(如昨日收盘价),lead()用于预判或对齐未来(如次日跳空缺口);二者时间方向相反,不可互换,必须配合partition by和order by使用,否则导致跨实体错乱。

直接说结论:LAG() 用于回溯历史(比如昨日收盘价、上期持仓),LEAD() 用于预判或对齐未来(比如次日跳空缺口、下一期违约风险)。二者不能互换,选错就等于时间方向反了。
什么时候必须用 LAG():计算“已发生”的变化量
金融场景里,所有“和过去比”的指标都依赖 LAG()。它取的是当前行之前的数据,天然适配滞后性分析。
- 日收益率 = (
close-LAG(close, 1)) /LAG(close, 1)—— 第一行结果为NULL,因为没“昨日收盘价” - 月度持仓变动 =
position-LAG(position, 1),但必须加PARTITION BY stock_code ORDER BY trade_date,否则跨股票混算 - 若用
LEAD()替代,会算成“明日减今日”,实际是明日收益率,无法用于当日归因 - 默认值慎设:设
0会导致分母为 0 报错;设1会扭曲真实比率;多数情况保留NULL更安全
什么时候必须用 LEAD():标记“将发生”的事件边界
LEAD() 不是用来预测未来值,而是把“下一行的已知事实”拉到当前行做判断——本质是错位对齐。
- 识别跳空缺口:比较
open和LEAD(close, 1)(即“今早开盘价 vs 昨日收盘价”),差值 > 2% 即标记 - 判断债券兑付风险:当前行是到期日,
LEAD(status, 1)是下一状态(如从 “正常” → “展期”),可提前 1 天触发预警 - 用户流失定义:若
LEAD(login_date, 1)与当前login_date间隔 > 90 天,且无中间记录,则当前行为最后一次登录 - 注意:
LEAD()在最后一行返回NULL,若业务要求“末尾视为已终止”,需显式写COALESCE(LEAD(...), 'END')
PARTITION BY 分组不加就踩坑
金融数据必有多维度:股票、基金、客户、账户。漏写 PARTITION BY 会让时间序列跨实体断裂。
- 错误写法:
LAG(nav) OVER (ORDER BY calc_date)—— 全表按日期排,A 基金的第 2 天可能接 B 基金的第 1 天 - 正确写法:
LAG(nav) OVER (PARTITION BY fund_code ORDER BY calc_date) - 复合分组常见:
PARTITION BY client_id, product_type,尤其在财富管理报表中 - Oracle/MySQL 8.0+ / SparkSQL 都支持,但旧版 MySQL(
性能与 NULL 处理的隐性成本
LAG() 和 LEAD() 本身不慢,但滥用默认值或忽略 NULL 会引发连锁问题。
- 大量
COALESCE(LEAD(x, 1), 0)会掩盖真实缺失——比如期货主力合约切换时,次日数据本应为空,填 0 就变成“价格归零” - 排序字段含重复值(如多笔同秒成交)必须加唯一键兜底:
ORDER BY trade_time, order_id,否则LAG()结果不稳定 - 大数据量时,
ORDER BY字段务必建索引,否则OVER子句会触发全局排序,拖慢十倍以上 - SparkSQL 中,
LEAD()在 shuffle 阶段需传输下一分区首行数据,跨 partition 性能损耗比LAG()略高
真正难的不是语法,而是想清楚:你要对比的“前”和“后”,在业务逻辑里到底属于哪个实体、按什么时间轴排列、缺失时该沉默还是该报警。时间方向错了,整个分析链就崩了。











