last_value默认只返回当前行,因其隐式使用rows between unbounded preceding and current row;要获取分组内真正最后值,须显式指定unbounded following;而first_value配合desc排序更安全可靠。

LAST_VALUE 默认窗口帧只到 CURRENT ROW
LAST_VALUE 返回当前行,不是 bug,是 SQL 标准定义的默认行为:它隐式使用 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 作为窗口帧。每计算一行,窗口就“截止到当前行”,当前行自然就是这个范围里的“最后一个”。哪怕你写了 ORDER BY updated_at DESC,它也只在已扫描过的行里找末值,而不是整个分组。
显式指定 UNBOUNDED FOLLOWING 才能覆盖整组
想让 LAST_VALUE 真正看到分组内所有行,必须手动补全窗口定义:
LAST_VALUE(col) OVER (PARTITION BY g ORDER BY t ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)- 仅写
PARTITION BY或仅加ORDER BY不够,缺了ROWS BETWEEN这一句,就还是当前行 - 某些引擎(如旧版 MySQL、SQLite)不支持
UNBOUNDED FOLLOWING,此时该写法直接报错或被忽略
FIRST_VALUE + DESC 比 LAST_VALUE 更安全
多数人真正想要的是“最新一条”,即“按时间倒序后的第一条”。FIRST_VALUE 天然匹配这个语义,且默认帧就足够:
-
FIRST_VALUE(status) OVER (PARTITION BY user_id ORDER BY updated_at DESC)—— 不用额外写ROWS子句,漏写的概率低 - 排序字段重复时,务必加二级排序,例如:
ORDER BY updated_at DESC, id DESC -
FIRST_VALUE在 PostgreSQL、MySQL 8.0+、SQL Server、BigQuery 上行为一致;LAST_VALUE在 SQLite 中不支持帧子句,根本不可用
LAST_VALUE 不能取整行,强行套用会错位
LAST_VALUE 是逐列计算的函数,不是行级操作:
- 写十个
LAST_VALUE(x)、LAST_VALUE(y),它们可能来自不同物理行——尤其当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,还强依赖帧定义;而“最后一条记录”这个业务目标,往往被误认为只需一个函数就能解决——实际上它常需要排序确定性、去重兜底、以及行级筛选三者配合。










