first_value默认返回“当前行之前范围内的首值”而非分组第一行,因其默认窗口帧为range between unbounded preceding and current row;需显式指定rows between unbounded preceding and unbounded following并配合稳定order by(如时间+唯一id)才能准确获取分组首行值。

为什么 FIRST_VALUE 返回的不是你想要的“分组第一行”?
直接在 GROUP BY 后用 FIRST_VALUE 会报错——它根本不能和 GROUP BY 混用。窗口函数必须配合 OVER() 子句,且作用对象是结果集的逻辑分区,不是聚合后的行。
常见误解是把它当“每组取第一条”,但实际行为取决于 ORDER BY 和 frame_clause 的组合。默认框架是 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,这意味着如果排序字段有重复值,FIRST_VALUE 可能返回当前行之前任意一个“相同排序值”的首行,而非物理顺序第一行。
FIRST_VALUE 正确写法:必须显式定义 PARTITION BY 和 ORDER BY
要获取每个分组的首行特征(比如按时间最早、ID最小),关键在于:
• PARTITION BY 定义分组维度(如 user_id)
• ORDER BY 明确决定“谁是第一”(如 created_at ASC 或 id ASC)
• 建议加上 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING 消除默认框架干扰
- 错误写法:
SELECT FIRST_VALUE(name) OVER (PARTITION BY dept) FROM emp;(没ORDER BY,结果不确定) - 正确写法:
SELECT name, dept, FIRST_VALUE(salary) OVER (PARTITION BY dept ORDER BY hire_date ASC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS first_hire_salary FROM emp; - 若需稳定取物理首行,可加唯一排序字段:
ORDER BY hire_date ASC, id ASC
和 ROW_NUMBER() = 1 对比:什么时候该用哪个?
FIRST_VALUE 是复制值,不改变行数;ROW_NUMBER() 是标记序号,常配合 WHERE 过滤出首行。两者适用场景不同:
- 想在每行都显示本组“最早入职者薪资” → 用
FIRST_VALUE - 只想查出每组最早入职的那条完整记录 → 用
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY hire_date ASC) = 1 -
FIRST_VALUE无法直接过滤行,也不能处理“取首行多列”的需求(得套子查询或多次调用) - 性能上,
FIRST_VALUE需扫描整个窗口,而ROW_NUMBER+WHERE可能提前终止(取决于优化器)
容易被忽略的 NULL 处理和类型兼容性
FIRST_VALUE 遇到全为 NULL 的分区时,返回 NULL;但如果排序字段含 NULL,不同数据库对 NULLS FIRST/LAST 默认处理不同(PostgreSQL 默认 NULLS FIRST,MySQL 8.0+ 默认 NULLS LAST)。这直接影响哪一行被当作“第一”。
- 显式声明:
ORDER BY status NULLS LAST确保有效值优先 - 类型必须一致:
FIRST_VALUE(created_at)和FIRST_VALUE(user_name)不能在同一窗口中混用不同列类型 - 某些旧版 Hive 或 Spark SQL 不支持
ROWS BETWEEN语法,需查文档确认支持范围
真正麻烦的不是语法,而是排序键设计——如果业务上“首行”依赖多个条件(比如状态优先、再按时间),ORDER BY 的表达式必须严格反映这个优先级,否则 FIRST_VALUE 就只是个看似正确实则错位的值。











