first_value函数必须配合over()窗口子句使用,单独调用语法不合法;其返回值由partition by分组和order by排序共同决定,且默认受null处理策略影响。

FIRST_VALUE函数必须配合窗口定义才能生效
单独写 FIRST_VALUE(column) 不会返回分组首条,它根本不会执行——SQL 会直接报错或返回 NULL(取决于数据库和 NULL 处理策略)。这个函数本质是窗口函数,必须搭配 OVER() 子句,否则语法不合法。
常见错误现象:ERROR: window function calls require an OVER clause(PostgreSQL)、ORA-30483: window functions are not allowed here(Oracle)。
正确用法要点:
-
OVER()里至少要写PARTITION BY(分组)和ORDER BY(排序),否则“首条”无意义 - 如果只写
PARTITION BY不写ORDER BY,多数数据库(如 PostgreSQL、SQL Server)会报错;MySQL 8.0+ 允许但行为未定义,结果不可靠 -
ORDER BY决定哪一行是“第一行”,不是物理插入顺序,而是按你指定的列值升序排列后的首行
分组取首条时 ORDER BY 的方向很关键
FIRST_VALUE 总是取 ORDER BY 排序后窗口内的第一行,所以“首条”的含义完全由你写的 ORDER BY 决定。想取最新时间的数据?得写 ORDER BY created_at DESC;想取最早 ID?写 ORDER BY id ASC。
容易踩的坑:
- 误以为
FIRST_VALUE(name)默认按主键或插入顺序取,实际完全没依据 - 写成
ORDER BY updated_at ASC却想拿最新记录,结果拿到最老的一条 - 对 NULL 值没做处理:默认
NULLS FIRST或NULLS LAST因数据库而异(PostgreSQL 默认NULLS FIRST,Oracle 默认NULLS LAST),可能让 NULL 行意外成为“首条”
示例(取每组中 score 最高者的 name):
SELECT
student_id,
subject,
score,
FIRST_VALUE(name) OVER (
PARTITION BY subject
ORDER BY score DESC NULLS LAST
) AS top_student_name
FROM scores;
注意 FIRST_VALUE 和其他“首值”写法的区别
有人会用子查询 + ROW_NUMBER() 或 GROUP BY + 聚合来模拟首条,但语义和性能不同:
-
FIRST_VALUE是窗口函数,保留原始行数,每行都带对应分组的首条值,适合做横向参考 -
ROW_NUMBER() = 1是过滤手段,会删掉非首行,适合只取一条代表记录 -
GROUP BY+MIN()/MAX()只能取聚合值,无法带回同一行的其他字段(比如你不能直接用MAX(score)同时拿到该行的name) - 某些数据库(如 MySQL 5.7)不支持窗口函数,这时
FIRST_VALUE根本不可用,得换方案
性能提示:在大表上,PARTITION BY 字段若无索引,ORDER BY 排序成本可能很高;建议在 PARTITION BY + ORDER BY 组合列上建联合索引。
不同数据库对 FIRST_VALUE 的 NULL 处理不一致
当窗口内所有值都是 NULL 时,FIRST_VALUE 返回 NULL —— 这点各库一致。但更隐蔽的问题是:如果 ORDER BY 列含 NULL,且你没显式声明 NULLS FIRST/LAST,结果可能跨库不兼容。
- PostgreSQL 默认
NULLS FIRST,所以 NULL 行排最前,FIRST_VALUE就取到 NULL - Oracle、SQL Server 默认
NULLS LAST,NULL 行被排到最后,不影响首条选取 - MySQL 8.0+ 支持
NULLS FIRST/LAST,但旧版本忽略,行为依赖排序规则
稳妥做法:只要 ORDER BY 列可能为 NULL,就显式加上 NULLS LAST(想排除 NULL 干扰时)或 NULLS FIRST(想优先选 NULL 时)。
真正麻烦的是业务逻辑里没意识到排序字段有 NULL,测试数据恰好没 NULL,上线后某天出现脏数据,FIRST_VALUE 结果突然变化——这种问题很难复现,也最难排查。










