lag()不能直接查断号,需配合order by id计算id - lag(id) > 1定位缺口;或结合row_number()做差分分组,以grp突变处识别断点起始,适用于整型自增主键。

LAG() 本身不能直接查断号,但能帮你定位“连续段边界”
直接用 LAG() 查断号是常见误解——它只返回前一行的值,不自动识别缺失。真正有用的是:先用 LAG() 拿到「上一个 ID」,再和当前 ID 做差值判断是否跳变。比如 ID 序列为 1,2,3,5,6,当当前行 ID=5、LAG(id)=3 时,差值为 2,说明中间缺了 4。
- 必须配合
ORDER BY id,否则LAG()返回的“上一个”毫无业务意义 - 差值判断逻辑要写成
id - LAG(id) OVER (ORDER BY id) > 1,不能只写!= 1(避免负数或 NULL 干扰) - 首行的
LAG(id)默认为NULL,会导致id - NULL整行为NULL,需用LAG(id, 1, 0)设默认值兜底
用 LAG() + ROW_NUMBER() 组合识别断号起始点
比单纯看差值更稳的做法:把原始 ID 和按顺序生成的行号做差,相同差值代表同一连续段;差值突变处就是断点开始位置。这个技巧本质是把“ID 连续性”映射为“分组稳定性”。
- 写法示例:
id - ROW_NUMBER() OVER (ORDER BY id) AS grp - 再对
grp分组,取每组MIN(id)和MAX(id),就能知道每个连续段范围 - 断号起始 = 下一段的
MIN(id)- 1,比如段 A 是 1–3,段 B 是 5–6,则断号起始是 4 - 注意:该方法要求 ID 是整数且单调递增,若含负数或非数字字符会出错
为什么不用 LAG() 直接替代 NOT EXISTS 或 LEFT JOIN?
因为 LAG() 是窗口函数,只能看到“已有数据中的前一行”,无法感知“根本不存在的 ID”。比如 ID 最大是 100,但实际缺失 99 和 101,LAG() 根本接触不到 101 —— 它不在结果集里。
-
NOT EXISTS和LEFT JOIN是集合操作,能主动构造并验证“ID+1 是否存在” -
LAG()更适合已知数据完整、只需检查相邻关系的场景(如财务流水余额校验) - 混合使用才高效:先用
LAG()快速扫一遍小表找明显跳变,再对疑似区间用NOT EXISTS精确补全缺失值
MySQL 8.0+ 中 LAG() 的坑:ORDER BY 字段没索引,查询就卡死
窗口函数执行前会强制排序,如果 ORDER BY id 的字段没索引,10 万行数据可能从 50ms 拉长到 8 秒以上。这不是语法问题,是执行计划失控。
- 确认索引存在:
SHOW INDEX FROM numbers WHERE Key_name = 'PRIMARY' OR Column_name = 'id'; - 别依赖主键自动覆盖:复合主键或联合索引中
id不在最左位时,ORDER BY id仍可能触发 filesort - 测试时加
EXPLAIN,重点看Extra列是否含Using filesort或Using temporary
真正难的不是写对 LAG() 语法,而是想清楚你要查的是“相邻两行的间隙”,还是“整个值域内的空缺”——前者用 LAG() 快,后者必须用子查询或连接。










