结论:Spark SQL 3.0+ 支持 IGNORE NULLS,仅限于 lag、lead、first_value、last_value 等特定窗口函数;2.x 及更早版本不支持,需用 row_number 或自定义逻辑模拟。

窗口函数里 IGNORE NULLS 真的可用吗?
直接说结论:IGNORE NULLS 在标准 SQL 的窗口函数中不被支持,它只存在于某些数据库的特定函数中(比如 LAG、LEAD、FIRST_VALUE 等的参数列表),且仅限于 Oracle、BigQuery 和 PostgreSQL 16+(部分支持)。MySQL、SQL Server、SQLite 完全不识别这个语法,写上去会报错 ERROR: syntax error at or near "IGNORE" 或类似提示。
这意味着你不能在 OVER() 子句里加 IGNORE NULLS,也不能把它当作窗口定义的一部分——它只能作为某些**标量窗口函数的可选修饰符**出现。
LAG 和 LEAD 中的 IGNORE NULLS 怎么用?
这是最常遇到的场景:你想跳过前面/后面的 NULL 值,取最近一个非 NULL 的值。PostgreSQL 16+ 和 BigQuery 支持该参数,Oracle 更早就有。
-
LAG(col, offset, default) IGNORE NULLS表示“往前找第offset个非 NULL 值”,不是跳过offset行再忽略 NULL - 如果找不到非 NULL 值,返回
default(若未指定则为 NULL) - 注意:PostgreSQL 要求
IGNORE NULLS必须紧接在参数列表后,不能加逗号分隔;BigQuery 允许写成LAG(col) IGNORE NULLS(省略 offset/default)
示例(PostgreSQL 16+):
SELECT id, val, LAG(val) IGNORE NULLS OVER (ORDER BY id) AS prev_non_null FROM (VALUES (1,10), (2,NULL), (3,20), (4,NULL), (5,30)) t(id,val);
结果中 id=3 的 prev_non_null 是 10(跳过 id=2 的 NULL),id=5 的是 20(跳过 id=4 的 NULL)。
没有 IGNORE NULLS 怎么办?兼容方案有哪些?
在 MySQL、SQL Server 或老版本 PostgreSQL 中,必须手动模拟“跳过 NULL”的逻辑。核心思路是:先用条件标记非 NULL 行的序号,再关联上最近的前一个序号。
- 用
ROW_NUMBER() OVER (PARTITION BY (CASE WHEN col IS NOT NULL THEN 1 END) ORDER BY ...)不行——NULL 会被单独分组,无法跨组引用 - 正确做法:生成一个只针对非 NULL 值的连续序号(比如用
SUM(CASE WHEN col IS NOT NULL THEN 1 ELSE 0 END) OVER (ORDER BY ...)),再自连接或用LAST_VALUE(... RESPECT NULLS)配合范围帧 - 更简单但稍低效的写法:用相关子查询
(SELECT val FROM t t2 WHERE t2.id - SQL Server 可用
LAST_VALUE+ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING,但必须配合RESPECT NULLS(默认行为),再用外层COALESCE回退到更早值——实际仍需多层嵌套
为什么 FIRST_VALUE 加 IGNORE NULLS 还是返回 NULL?
常见误解:以为 FIRST_VALUE(col IGNORE NULLS) 能自动跳过 NULL。但实际语法是 FIRST_VALUE(col) IGNORE NULLS,且仅当窗口帧内存在非 NULL 值时才生效。容易踩的坑有:
- 窗口帧太小(如
ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING),刚好框住的全是 NULL - 排序字段本身含 NULL,导致
ORDER BY结果不稳定,非 NULL 值没排到前面 - PostgreSQL 中
IGNORE NULLS对FIRST_VALUE的支持是 16.0 才引入的,15 及以前版本忽略该关键字,按默认行为返回第一个值(可能是 NULL) - BigQuery 中它只影响
LEAD/LAG,FIRST_VALUE不接受IGNORE NULLS,强行写会报错
真正稳妥的做法,永远先确认你的数据库版本和文档——IGNORE NULLS 不是通用开关,而是少数函数在少数版本里的“特供参数”。漏看版本要求,比写错语法更容易导致静默错误。










