last_value默认只返回当前行值,因其隐式窗口帧为rows between unbounded preceding and current row;要取分组内真实末值需显式指定unbounded following,但兼容性差;推荐用first_value配合order by ... desc更安全统一。

LAST_VALUE 默认只看“当前行及之前”
LAST_VALUE 返回当前行,不是 bug,是 SQL 标准定义的默认行为:它的隐式窗口帧为 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW。每计算一行,窗口就截止到这一行,当前行自然就是这个小范围里的“最后一个”。哪怕你写了 ORDER BY updated_at DESC,它也只在已扫描过的行里找,不会往后看。
漏写 ROWS BETWEEN 就掉进默认帧陷阱
常见错误是只写 PARTITION BY 和 ORDER BY,却没显式指定帧边界:
-
LAST_VALUE(status) OVER (PARTITION BY user_id ORDER BY updated_at)→ 实际等价于加了默认帧,结果每行都等于本行status - 想取分组内真实末值,必须补全:
LAST_VALUE(status) OVER (PARTITION BY user_id ORDER BY updated_at ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) - 某些引擎(如旧版 MySQL 或 SQLite)根本不支持
UNBOUNDED FOLLOWING,此写法直接报错或静默忽略
用 FIRST_VALUE + DESC 更安全、更少出错
多数人真正想要的是“最新一条”,本质是“按时间倒序后的第一条”。FIRST_VALUE 天然匹配该语义,且默认帧就足够:
-
FIRST_VALUE(amount) OVER (PARTITION BY user_id ORDER BY updated_at DESC)不需要额外写ROWS BETWEEN - 排序方向和业务意图一致,不易混淆;各主流引擎对
FIRST_VALUE的实现比LAST_VALUE更统一 - 若字段可能为
NULL,建议显式加NULLS LAST(PostgreSQL)或提前过滤(MySQL)
LAST_VALUE 不能直接取整行,别硬套
LAST_VALUE 是逐列计算的函数,不是记录筛选器:
- 写十个
LAST_VALUE(col1)、LAST_VALUE(col2),它们可能来自不同物理行——尤其当ORDER BY字段不唯一时 - 无法满足“取最新订单的
id+amount+status”这种需求 - 真要取完整记录,优先用:
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY updated_at DESC, id DESC) AS rn,再外层WHERE rn = 1
ORDER BY,还强依赖帧定义;而“最后一条记录”这个业务目标,常被误认为只需一个函数就能解决——实际上它常需要排序确定性、去重兜底、以及行级筛。










