FIRST_VALUE不跳过NULL,返回排序后首行值(含NULL);需用CASE WHEN将NULL排末尾并辅以确定性字段(如id)排序,才能获取首个非空值。

为什么 FIRST_VALUE 无法直接过滤空值
FIRST_VALUE 是窗口函数,它只按 ORDER BY 排序后取第一个值,**不跳过 NULL**。如果你的排序列本身含空(比如 updated_at 为 NULL),那 FIRST_VALUE(updated_at) 很可能就返回 NULL——这不是你想要的“第一条非空记录”,而是“排序后位置第一的值”。
用 FILTER 子句(PostgreSQL)或条件表达式绕过 NULL
PostgreSQL 14+ 支持在聚合/窗口函数中用 FILTER,但注意:FIRST_VALUE **不支持 FILTER**。必须换思路:
- 把空值排到末尾:在
ORDER BY中用CASE WHEN updated_at IS NULL THEN 1 ELSE 0 END, updated_at DESC - 或者更稳妥:先用
ROW_NUMBER()标记每组中非空记录的顺序,再用条件聚合提取
推荐写法(兼容多数数据库):
SELECT
group_id,
FIRST_VALUE(value) OVER (
PARTITION BY group_id
ORDER BY CASE WHEN value IS NOT NULL THEN 0 ELSE 1 END, id
) AS first_nonnull_value
FROM t;
这里 id 是辅助排序字段(如主键或时间戳),确保非空值内部也有确定顺序。
MySQL / SQL Server 怎么办:用子查询 + LIMIT/TOP 模拟
MySQL 8.0+ 和 SQL Server 不支持在 FIRST_VALUE 中灵活跳过 NULL,但可以用相关子查询实现等效逻辑:
- 对每个
group_id,查出value IS NOT NULL的最小id对应的value - 避免用
MIN(value)——那是聚合值,不是“第一条”的原始值
MySQL 示例:
SELECT DISTINCT t1.group_id, (SELECT t2.value FROM t t2 WHERE t2.group_id = t1.group_id AND t2.value IS NOT NULL ORDER BY t2.id LIMIT 1) AS first_nonnull_value FROM t t1;
容易忽略的边界情况
实际跑起来常踩这几个坑:
- 所有记录的
value都是NULL→ 上述FIRST_VALUE写法仍会返回NULL,但子查询方式会返回NULL(无匹配行),行为一致;若需返回默认值,得套COALESCE - 没指定稳定排序(如只用
ORDER BY value)→ 相同value时数据库可能任意选一行,结果不可复现 - 误用
LAST_VALUE并以为能反向跳过 NULL → 它同样不跳 NULL,只是取窗口末尾位置的值
真正可靠的“第一条非空”,核心不是函数名,而是你怎么定义“第一条”——得靠 ORDER BY 的构造把 NULL 排出去,再用确定性字段收尾。

















