FIRST_VALUE常返回整表第一个值是因为未指定PARTITION BY和ORDER BY,导致默认作用于全表;必须显式声明分组与排序逻辑才能按分组内指定顺序取首值。

FIRST_VALUE 为什么常返回整表第一个值而不是分组内首个
直接写 FIRST_VALUE(price) 却发现结果全是同一行的 price,不是每个分组内最小时间对应的值?问题出在窗口定义——没加 OVER 子句里的 PARTITION BY 和 ORDER BY。SQL 默认把整个结果集当一个窗口,FIRST_VALUE 就真取了全表排序后第一行的值。
必须显式声明分组和排序逻辑,否则它根本不知道“分组内”指什么、“首个”按什么顺序判:
-
PARTITION BY category划定分组边界(比如按商品类目) -
ORDER BY created_at决定“首个”的依据(时间最早?ID 最小?) - 缺一不可;只写
PARTITION BY不排序,结果不确定(依赖引擎实现)
ORDER BY 后必须加 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 吗
不加也行,但得清楚默认行为:FIRST_VALUE 默认窗口帧是 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING,也就是“看整个分组”。这通常符合需求——你要的就是该分组里排第一的那个值。
只有当你需要动态“截止到当前行为止的第一值”(比如累计首单金额),才需显式改帧。多数场景下,省略帧定义更安全、更易读:
SELECT id, category, price, FIRST_VALUE(price) OVER (PARTITION BY category ORDER BY created_at) AS first_price_in_cat FROM products;
遇到 NULL 值时 FIRST_VALUE 会跳过吗
不会跳过。FIRST_VALUE 按 ORDER BY 排序后取第一行的原始值,哪怕那一行的 price 是 NULL,结果就是 NULL。如果想跳过 NULL 取第一个非空值,得先过滤或用条件表达式预处理:
- 在
ORDER BY中把 NULL 排后面:ORDER BY price IS NULL, price - 或用
CASE构造辅助排序字段:ORDER BY CASE WHEN price IS NULL THEN 1 ELSE 0 END, price - 注意:不能在
FIRST_VALUE内部用WHERE过滤,窗口函数执行时 WHERE 已完成
和 MIN/MAX 比较:FIRST_VALUE 能替代它们吗
不能直接替代。MIN(price) 返回分组内最低价格,而 FIRST_VALUE(price) OVER (ORDER BY price) 返回价格最低那行的 price —— 看似一样,但关键区别在于:前者只关心值,后者还隐含了“该值来自哪一行”。如果你后续要关联这行的其他字段(比如最低价对应的 product_name),FIRST_VALUE 就比聚合函数更有价值。
典型误用:
- 只取数值用
MIN更高效,无需窗口开销 - 要取“最早下单的用户昵称”,就必须用
FIRST_VALUE(nickname) OVER (ORDER BY order_time) - 若
ORDER BY字段有重复值(多个同时间订单),FIRST_VALUE返回其中任意一行(无稳定性保证),需加次级排序(如ORDER BY order_time, id)才能确定
实际用的时候,最容易被忽略的是 ORDER BY 的稳定性——没加唯一键兜底时,相同排序值的行顺序由数据库内部决定,每次执行结果可能不同。










