FIRST_VALUE()在MySQL 8.0中必须配合ORDER BY使用,否则报错;它仅按指定逻辑顺序取值,不保证物理顺序,排序字段含NULL或重复值时结果不稳定,取整行应优先用ROW_NUMBER()。

必须写 ORDER BY,否则直接报错
FIRST_VALUE() 在 MySQL 8.0 中是强制要求 ORDER BY 的窗口子句,缺了就报语法错误:ERROR 3586 (HY000): Window '<unnamed window>' lacks an ORDER BY clause</unnamed>。它不认“插入顺序”或“物理存储顺序”,只按你写的排序逻辑取值。
常见误写:FIRST_VALUE(name) OVER (PARTITION BY dept_id) —— 这在 MySQL 8.0 下根本跑不起来。
正确做法是明确指定排序依据,比如按时间升序取最早一条:FIRST_VALUE(name) OVER (PARTITION BY dept_id ORDER BY created_at ASC);若按薪资降序取最高者,则用 ORDER BY salary DESC。
排序字段含重复值时结果不稳定,建议加唯一字段兜底,例如:ORDER BY created_at ASC, id ASC。
取整行数据?FIRST_VALUE 不行,别硬套多个
FIRST_VALUE() 是标量函数,一次只能返回一个表达式的值。你想拿“首条记录的 id、name、amount 全部字段”,不能靠写三遍 FIRST_VALUE(id)、FIRST_VALUE(name)、FIRST_VALUE(amount) —— 如果排序字段有重复(比如多个 created_at 相同),这三个调用可能各自取到不同行,造成字段错位。
更糟的是,这种写法无法过滤出“只有首行”的结果集,它只是给每行都补一个首值,行数不变。
真正要拿到完整首行,应该用:ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY created_at ASC, id ASC) AS rn,再外层 WHERE rn = 1。
NULL 值会让 FIRST_VALUE 返回意料之外的结果
如果排序字段(如 created_at)允许为 NULL,MySQL 8.0 默认把 NULL 排在最前面(ASC 时),那么 FIRST_VALUE(name) 很可能返回 NULL 对应那行的 name,而你以为是“没数据”或“查错了”。
这不是 bug,是确定行为:只要排序字段为 NULL,且排在第一,它就被当成“首行”。
应对方式有两种:
- 提前用 COALESCE(created_at, '9999-12-31') 把 NULL 挤到末尾(配合 ASC)
- 或在业务层确保排序字段非空,比如建表时加 NOT NULL 约束 + 默认值
性能比 GROUP BY + 子查询差,尤其没索引时
FIRST_VALUE() 必须对每个分组做完整窗口排序,即使你只关心首行,优化器也无法跳过其余行的排序。当分组多、每组数据大时,开销明显。
对比方案:SELECT * FROM t1 WHERE id IN (SELECT MIN(id) FROM t2 GROUP BY dept_id),如果 (dept_id, id) 上有复合索引,可能走索引扫描,完全避开排序。
验证方法:用 EXPLAIN FORMAT=TREE 看执行计划,注意是否有 windowing 步骤,以及它的 cost 是否显著高于 range 扫描。
索引建议:对常用分组+排序组合建联合索引,比如 INDEX(dept_id, created_at, id),能同时加速 PARTITION BY 和 ORDER BY。


















