first_value返回的不是物理首行,因为它只按order by定义的逻辑顺序取值,未指定order by则报错,排序字段重复或含null时结果不确定;必须用稳定排序(如order by created_at, id)并显式指定窗口帧才能确保一致性。

FIRST_VALUE 为什么返回的不是表里第一条?
因为 FIRST_VALUE 是窗口函数,它不看物理存储顺序,只看 ORDER BY 子句定义的逻辑顺序。没写 ORDER BY 就直接报错;写了但排序字段有重复值,结果就可能“飘”——比如按 status 排序,多个 'active' 行,数据库选哪个当“第一”是不确定的。
常见错误现象:FIRST_VALUE(name) OVER (PARTITION BY dept_id) 没加 ORDER BY → 直接报错;加了 ORDER BY created_at 但多个记录时间相同 → 每次执行结果可能不同。
- 必须搭配
ORDER BY,哪怕只是ORDER BY id这种主键字段 - 如果想稳定取“插入顺序第一条”,确保排序字段能唯一确定顺序(如自增
id或带毫秒的created_at) -
PARTITION BY不加也行,此时整个结果集视为一个分区
怎么用 FIRST_VALUE 取每个分组的首条记录?
典型场景:查每个部门薪资最高的员工姓名(注意不是最高薪资,而是对应那条记录的姓名)。这时候不能只靠 MAX(salary),得把整行关联信息带出来。
实操建议:用 FIRST_VALUE 配合 PARTITION BY dept_id ORDER BY salary DESC,再用 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING 确保窗口覆盖全分区(默认就是这个范围,可省略)。
- 排序方向很重要:
ORDER BY salary DESC才能拿到最高薪那条的name,反过来就变成最低薪的 - 别漏掉
PARTITION BY,否则所有部门混在一起只算一个“首条” - 如果要取多列(比如姓名+薪资),得对每列分别写
FIRST_VALUE,不能一次取整行
SELECT dept_id, name, salary, FIRST_VALUE(name) OVER (PARTITION BY dept_id ORDER BY salary DESC) AS top_name, FIRST_VALUE(salary) OVER (PARTITION BY dept_id ORDER BY salary DESC) AS top_salary FROM employees;
FIRST_VALUE 和 LIMIT 1 / ROW_NUMBER() 有什么区别?
FIRST_VALUE 是“复制首条值到当前行”,不是过滤出首条记录。很多人误以为它能替代 ROW_NUMBER() = 1 的筛选逻辑,结果发现返回行数没变,只是多了一列重复值。
性能影响:在大数据集上,FIRST_VALUE 要为每一行计算窗口,而 ROW_NUMBER() + WHERE rnk = 1 可以配合索引快速定位,后者通常更快、更省内存。
- 想“取出”首条记录 → 用
ROW_NUMBER() OVER (...) AS rnk+WHERE rnk = 1 - 想“标记”当前行所属分组的首条值 → 才用
FIRST_VALUE - MySQL 8.0+、PostgreSQL、SQL Server 都支持;SQLite 支持但版本需 ≥ 3.25;旧版 MySQL 不支持窗口函数
容易被忽略的 NULL 处理和数据类型陷阱
FIRST_VALUE 默认跳过 NULL 值——如果排序字段是 NULL,那行会被排除在窗口排序外,可能导致你预期的“第一条”实际被跳过。另外,返回值类型严格继承源列,比如 FIRST_VALUE(price) 返回 DECIMAL,但若和 INT 字段做运算可能隐式转换失败。
- 显式控制
NULL排序位置:ORDER BY status NULLS LAST(PostgreSQL/Oracle)或ORDER BY IFNULL(status, 'zzz')(MySQL) - 避免跨类型比较:不要拿
FIRST_VALUE(name)直接跟数字字段=判断 - 某些数据库(如早期 SQL Server)对
FIRST_VALUE的RESPECT NULLS/IGNORE NULLS参数支持不一致,建议统一用COALESCE预处理源字段











