LAG()本身不跳过NULL,需用MAX(CASE WHEN val IS NOT NULL THEN val END) OVER (ORDER BY id ROWS UNBOUNDED PRECEDING)或FIRST_VALUE(val) IGNORE NULLS实现前向非空填充,前者兼容性强、性能稳定,后者仅限PostgreSQL 14+等支持引擎。

LAG() 本身不跳过 NULL,必须用窗口函数组合实现
LAG() 是严格按行偏移取值的,遇到 NULL 就返回 NULL,它不会自动“跳过”空值去找上一个非空。想拿到前一个非空值,得靠 MAX() OVER 或 FIRST_VALUE() OVER 配合条件过滤来模拟“向前填充”逻辑。
用 MAX() OVER + CASE 实现前向非空填充(推荐)
核心思路:把所有非空值往前广播,再按当前行取最大(即最近一次出现的非空值)。适合大多数数据库(PostgreSQL、SQL Server、Oracle、BigQuery),语法简洁且性能可控。
假设表 t 有列 id(顺序)、val(可能为 NULL),要填 last_non_null_val:
SELECT id, val,
MAX(CASE WHEN val IS NOT NULL THEN val END)
OVER (ORDER BY id ROWS UNBOUNDED PRECEDING) AS last_non_null_val
FROM t;
注意点:
-
ROWS UNBOUNDED PRECEDING必须显式写,否则默认RANGE在某些数据库(如 PostgreSQL)下行为不同 - 如果
val是字符串或数字,MAX()没问题;但如果是 JSON 或复杂类型,需确认数据库支持该类型比较 - 首行若
val为NULL,结果仍是NULL——这是合理行为,不是 bug
FIRST_VALUE() + IGNORE NULLS(仅限支持该语法的引擎)
PostgreSQL 14+、Oracle、Snowflake 和 BigQuery 支持 FIRST_VALUE(val) IGNORE NULLS OVER (...),语义更直接:
SELECT id, val,
FIRST_VALUE(val) IGNORE NULLS
OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS last_non_null_val
FROM t;
但要注意:
- MySQL 8.0 不支持
IGNORE NULLS,强行写会报错ERROR 3589 (HY000): Window function 'FIRST_VALUE' does not support IGNORE NULLS - SQL Server 完全不支持
IGNORE NULLS,哪怕最新版 - 即使支持,
FIRST_VALUE() IGNORE NULLS在大数据量时可能比MAX(CASE...)稍慢,因需维护非空值栈
别用 LAG() 嵌套或递归 CTE 来“跳过 NULL”
有人试图用 LAG(val, 1)、LAG(val, 2) 多层嵌套判断,或者写递归 CTE 向前扫描——这两种方式在真实业务中几乎不可行:
- 嵌套
LAG()最多能处理固定层数,一旦连续空值超过预设,就失效 - 递归 CTE 在多数数据库里无法并行,10 万行以上就明显卡顿,且 PostgreSQL 要开
max_recursive_iterations,MySQL 根本不支持 - 窗口函数方案是单次扫描,O(n) 时间复杂度;递归是 O(n²) 量级,数据一多就暴露
真正难的不是写出能跑的 SQL,而是让这个“前一个非空值”在分组(PARTITION BY)、排序不稳定(比如时间戳重复)、或存在大量连续 NULL 的场景下仍保持正确——这时候 MAX(CASE...) 的稳定性优势就很明显了。

















