first_value()不返回物理第一条记录,因其严格依赖order by定义的逻辑顺序;缺order by会报错,排序字段含重复值或null时结果不稳定,需用created_at, id等组合加唯一性保障,且无法直接返回整行,须配合row_number()筛选。

为什么FIRST_VALUE()返回的不是你想要的“第一条”?
因为FIRST_VALUE()不保证物理顺序,它只按ORDER BY子句定义的逻辑顺序取第一个值。如果你没写ORDER BY,MySQL 8.0会报错;如果写了但排序字段有重复值(比如多个记录created_at相同),结果可能不稳定——同一查询多次执行,返回的“首条”可能不同。
- 必须搭配
OVER (PARTITION BY ... ORDER BY ...)使用,缺一不可 - 排序字段建议加上唯一性约束(如
id或created_at, id组合)来消除歧义 -
FIRST_VALUE(col) OVER (PARTITION BY dept_id ORDER BY created_at, id)比单用created_at更可靠
怎么用FIRST_VALUE()拿到完整首条记录的全部字段?
FIRST_VALUE()只能返回单个表达式的值,不能直接返回整行。想拿“首条记录的所有列”,得配合ROW_NUMBER()或用子查询+IN关联。最稳妥的做法是:先用ROW_NUMBER()标记每组第一条,再过滤。
- 错误写法:
FIRST_VALUE(*)—— 语法不支持 - 可行写法:
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY created_at, id) AS rn,然后WHERE rn = 1 - 若坚持用
FIRST_VALUE()拼凑多字段,需对每个字段单独调用,且排序逻辑必须完全一致,否则字段可能来自不同行
和GROUP BY + MIN(id)方案比,FIRST_VALUE()有什么实际代价?
在大数据量下,FIRST_VALUE()需要窗口排序,而GROUP BY配合子查询可能走索引更快。尤其当分组键上有复合索引(如(dept_id, created_at, id))时,MIN(id)查法往往比窗口函数少做一次全排序。
-
FIRST_VALUE()强制触发窗口计算,即使只取首行,也要对每组所有行排序 -
GROUP BY dept_id JOIN ... WHERE t1.id = (SELECT MIN(t2.id) FROM t2 WHERE t2.dept_id = t1.dept_id)可能利用索引跳过排序 - 测试时用
EXPLAIN FORMAT=TREE对比执行计划,注意windowing步骤的cost是否显著高于range扫描
NULL值会让FIRST_VALUE()返回意外结果吗?
会。FIRST_VALUE(col)默认忽略NULL(即RESPECT NULLS是默认行为),但如果排序字段本身为NULL,这些记录会被排在最前或最后(取决于ORDER BY ... ASC/DESC),导致FIRST_VALUE()取到NULL值——这常被误认为“没数据”。
- 确认排序字段是否允许
NULL:SHOW COLUMNS FROM table_name LIKE 'created_at' - 安全做法:在
ORDER BY中显式控制NULL位置,例如ORDER BY created_at ASC NULLS LAST - 若业务上
created_at不应为空,建表时加NOT NULL约束,避免后期排查陷阱
OVER子句里的ORDER BY字段选错,轻则结果漂移,重则慢得不敢上线。











