lag()和lead()不直接比对两行,而是将当前行与偏移后的行字段拉至同一行,再通过普通表达式计算差值;必须指定order by保证顺序,建议加二级排序防重复;首尾行返回null,需用coalesce或where过滤处理;跨用户等分组场景须配合partition by,否则逻辑错乱。

用 LAG() 或 LEAD() 拿相邻行做差值比较
窗口函数本身不直接“比对两行”,而是让你把某一行的字段和它前/后一行的对应字段拉到同一行里,再用普通表达式计算差异。最常用的是 LAG()(取上一行)和 LEAD()(取下一行)。比如想看每个订单金额和上一个订单的差额:
SELECT id, amount, amount - LAG(amount) OVER (ORDER BY created_at) AS diff_from_prev FROM orders;
注意:必须明确指定 OVER (ORDER BY ...),否则结果无序、不可靠;如果 created_at 有重复值,建议加二级排序(如 id),避免窗口帧不稳定。
遇到 NULL 差值时怎么处理?
LAG() 对第一行返回 NULL,LEAD() 对最后一行也返回 NULL,直接相减会得到 NULL,不是 0。实际中常需要补默认值或跳过:
- 用
COALESCE(LAG(amount), 0)把首行差值设为amount - 0 - 用
WHERE diff_from_prev IS NOT NULL过滤掉首行(如果只关心变化) - 避免写成
amount - LAG(amount, 1, 0)—— 第三个参数在多数数据库(如 PostgreSQL、SQL Server)中不被支持,MySQL 8.0+ 才支持默认值参数
跨多列比对时别漏掉 PARTITION BY
如果数据按用户分组(比如每个用户的操作日志),想比对“同一用户内相邻操作”,必须加 PARTITION BY user_id,否则会把不同用户的记录混在一起排序:
一款AI工具,主要用于在主代理响应前,并行运行Kimi K2.5和GPT 5.3 Codex,注入双方观点以增强认知多样性,适合需要提升相关任务效率的用户。
SELECT user_id, action, created_at, LAG(action) OVER (PARTITION BY user_id ORDER BY created_at) AS prev_action, created_at - LAG(created_at) OVER (PARTITION BY user_id ORDER BY created_at) AS sec_since_last FROM user_events;
没加 PARTITION BY 是常见错误,尤其在业务数据天然分组时;另外,ORDER BY 的字段类型要支持比较(时间戳比字符串时间安全得多)。
性能和兼容性要注意这些点
窗口函数在大多数现代 SQL 引擎(PostgreSQL 8.4+、MySQL 8.0+、SQL Server 2005+、Oracle、BigQuery、Trino)都支持,但 SQLite 直到 3.25.0 才支持,旧版会报错 no such function: LAG。性能方面:
- 只要
ORDER BY字段有索引,LAG/LEAD开销很小 - 避免在子查询里嵌套多层窗口函数,有些引擎(如早期 MySQL)优化不佳
- 如果只是比对固定两行(比如最新两条),用
LIMIT 2+ 应用层处理可能比窗口函数更直观
真正麻烦的是语义边界:比如“上一行”到底指物理顺序还是逻辑顺序,取决于你 ORDER BY 写得是否精确——时间字段精度不够、时区未归一、空值未处理,都会让差值结果错位。










