FIRST_VALUE 做什么
FIRST_VALUE(表达式) OVER (…) 取窗口帧内第一行的值,并把它广播到该分区的每一行。常用于「把组内最早或最靠前的记录字段带到本组每一行」做对比、填充或算差值。MySQL 8.0 与 PostgreSQL 8.4+ 均支持,同族还有 LAST_VALUE 与 NTH_VALUE。
基本信息
| 适用平台 | MySQL 8.0+ / PostgreSQL 8.4+(窗口函数) |
|---|---|
| 语法 | FIRST_VALUE(expr) OVER (PARTITION BY … ORDER BY …) |
| 返回值 | 帧内首行的值;帧为空时为 NULL |
| 同族函数 | LAST_VALUE、NTH_VALUE |
典型使用场景
- 首单时间:把每个用户第一笔订单时间广播到该用户所有订单行
- 起点对比:用当前行与组内首行算差值、判断是否首单
- 报表展平:分组取首行字段当作组属性,避免再 JOIN 一次
- 状态回填:用组内初始状态填充后续缺失字段
- 配合 DISTINCT 或行号取每组第一条明细
示例
示例
SELECT user_id, order_id, created_at,
ROW_NUMBER() OVER w AS rn, -- 组内序号,从 1 开始
FIRST_VALUE(created_at) OVER w AS first_at, -- 组内首行时间,每行都一样
LAST_VALUE(created_at) OVER w AS last_at_now -- 默认帧下等于当前行,见下表
FROM orders
WINDOW w AS (PARTITION BY user_id ORDER BY created_at);同族函数与陷阱
| 函数 | 取值位置 | 默认帧下的常见误解 |
|---|---|---|
| FIRST_VALUE | 帧内第一行 | 加了 ROWS 收窄帧后取的是帧首行,不再是分区首行 |
| LAST_VALUE | 帧内最后一行 | 默认帧到当前行,结果往往就是当前行 |
| NTH_VALUE(x, n) | 帧内第 n 行 | 帧内行数不足 n 时结果为 NULL |
默认帧是 RANGE UNBOUNDED PRECEDING AND CURRENT ROW,所以 FIRST_VALUE 取分区首行、LAST_VALUE 取当前行。要取分区最后一行需显式写 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING。
排序键有并列值时「首行」在并列组内不确定,结果可能随执行计划变化;补一个唯一的次级排序键(如主键)保证稳定。FIRST_VALUE 也不会跳过 NULL,首行是 NULL 时整组就广播 NULL。
MIN() 与 FIRST_VALUE 的区别:MIN 返回组内最小的那个值,FIRST_VALUE 返回排序后首行的取值。需要「时间最早的那一整条记录」时用 FIRST_VALUE 或行号筛选更直接,不必先用 MIN 再回表匹配。