first_value()必须配合over()窗口定义使用,不可单独调用或与group by混用;正确写法为partition by分组+order by排序,用于获取每组首行指定列值,支持关联同记录其他字段。

FIRST_VALUE() 必须配合窗口定义使用,单独写会报错
直接写 FIRST_VALUE(order_date) 会触发类似 ERROR: window function calls require an OVER clause 的错误。它不是聚合函数,不能脱离 OVER() 存在。关键在于:你得明确告诉数据库“按什么分组、按什么排序”。比如要取每个用户最早下单日期,就得按 user_id 分组、按 order_date 升序排,才能让 FIRST_VALUE() 拿到第一个。
常见误写:SELECT user_id, FIRST_VALUE(order_date) FROM orders GROUP BY user_id;
这既缺 OVER,又混用了窗口函数和 GROUP BY,语法直接不通过。
正确写法:用 PARTITION BY + ORDER BY 定义窗口
核心是把分组逻辑放进 PARTITION BY,把“最早”的逻辑交给 ORDER BY ... ASC。注意:默认窗口帧(frame)是 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,这对 FIRST_VALUE() 没影响——因为它只关心整个分区的第一行,不是当前行之前的首行。
- 取每个用户的最早下单日期:
SELECT user_id, order_date, FIRST_VALUE(order_date) OVER (PARTITION BY user_id ORDER BY order_date ASC) AS first_order_date FROM orders; - 如果只要结果去重,可套一层
DISTINCT或用GROUP BY配合子查询 - 注意
NULL值:若某用户订单的order_date全为NULL,FIRST_VALUE()返回NULL;若混有NULL和非NULL,取决于数据库的NULLS FIRST/LAST默认行为(PostgreSQL 默认NULLS FIRST,MySQL 8.0+ 默认NULLS LAST)
替代方案对比:MIN() 聚合函数更简单,但能力不同
如果只要最早的日期值,不用关联其他字段,MIN(order_date) 加 GROUP BY 更轻量、更易读:
SELECT user_id, MIN(order_date) AS first_order_date FROM orders GROUP BY user_id;
但 FIRST_VALUE() 的优势在于:能拿到该最早日期所在行的**其他列**(比如下单时的 product_id 或 amount),而 MIN() 只能返回日期本身。一旦需要“最早那笔订单的完整信息”,就必须用窗口函数。
容易忽略的兼容性细节
不是所有数据库都支持 FIRST_VALUE(),且行为略有差异:
- MySQL 8.0+、PostgreSQL 8.4+、SQL Server 2012+ 支持标准语法
- SQLite 3.25+ 支持,但不支持
RESPECT NULLS/IGNORE NULLS子句 - 旧版 MySQL(
- 如果需跨多个字段判断“最早”,比如先比日期、日期相同时再比时间戳,得写成
ORDER BY order_date ASC, order_time ASC
真正难的不是写对那一行 OVER,而是想清楚:你要的是“每个分组里排序后第一行的某个字段”,还是“每个分组里这个字段的最小值”——选错就绕远路。











