窗口函数不能直接对比两表差异,因其仅作用于单结果集内部,无法识别跨表语义;对比两表需用外连接(如FULL OUTER JOIN)或UNION ALL模拟,而LAG/LEAD适用于单表内按时间或序列的纵向差异分析。

窗口函数不适合直接做两表差异对比
直接用 LAG() 或 LEAD() 对比两个独立表(比如 orders_old 和 orders_new)的数据差异,本质上行不通。窗口函数只在**单个结果集内部**定义“相邻行”,它不连接、不匹配、不识别跨表语义。你不能写 LAG(amount) OVER (ORDER BY id) 然后指望它从另一张表里拉值——数据库根本不知道那张表存在。
外连接才是查两表差异的正确工具
找“左表有、右表无”或“右表有、左表无”的记录,必须靠连接逻辑,而 FULL OUTER JOIN 是最贴近业务意图的写法。虽然 MySQL 不原生支持 FULL OUTER JOIN,但可以用 UNION ALL 模拟:
SELECT 'left_only' AS source, id, amount FROM orders_old WHERE id NOT IN (SELECT id FROM orders_new) UNION ALL SELECT 'right_only' AS source, id, amount FROM orders_new WHERE id NOT IN (SELECT id FROM orders_old);
关键点:
-
UNION ALL不能换成UNION,否则重复行会被隐式去重,而“重复本身可能就是差异” - 两边字段数、顺序、类型必须严格一致,否则报错
ERROR 1222 -
ON条件里只放关联键(如id),别加status = status这类过滤逻辑,否则会漏掉真实差异
窗口函数真正起作用的地方:单表内版本对比
当你有一张表存了多次快照(比如配置变更日志、订单状态流、用户资料修订),且每条记录带时间戳和主键,这时 LAG() 才大显身手:
SELECT id, status, updated_at,
LAG(status) OVER (PARTITION BY id ORDER BY updated_at) AS prev_status,
updated_at - LAG(updated_at) OVER (PARTITION BY id ORDER BY updated_at) AS sec_since_last
FROM config_history;注意陷阱:
- 没加
PARTITION BY id→ 不同配置项混在一起比,结果完全错乱 -
ORDER BY updated_at有重复值 → 窗口帧不稳定,建议补, id作二级排序 - 直接写
status != LAG(status)→ 遇到NULL会返回NULL而非TRUE,得拆解判断
为什么有人误以为窗口函数能替代外连接?
混淆了“对比目标”:窗口函数解决的是「同一实体在不同时间点的差异」,外连接解决的是「同一维度下不同数据源的差异」。前者是纵向时间轴上的偏移,后者是横向数据源间的对齐。把 LAG() 塞进两表 JOIN 的子查询里,看似组合,实则多了一层不必要的嵌套,还容易因排序键不唯一或分区缺失引入静默错误。

















