last_value默认只返回当前行值,因其默认帧为rows between unbounded preceding and current row;要取分组内排序后末值,须显式指定unbounded following,或更推荐用first_value配合降序排序。

LAST_VALUE 默认窗口帧只到 CURRENT ROW
LAST_VALUE 返回当前行值不是 bug,是 SQL 标准定义的行为:默认窗口帧为 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW。它只看“从分区开头到当前行(含)”这段数据,当前行自然就是这个范围里的“最后一个”。哪怕你写了 ORDER BY updated_at DESC,只要没显式改帧,LAST_VALUE(status) 仍只在已扫过的行里找——结果几乎总等于本行 status。
要取整组末值必须显式写 UNBOUNDED FOLLOWING
想真正拿到分组内排序后的末值,必须手动补全窗口定义:
LAST_VALUE(col) OVER (PARTITION BY g ORDER BY t ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)- 仅写
ORDER BY或仅加PARTITION BY不够,缺了ROWS BETWEEN这一句,就还是当前行 - 某些引擎(如旧版 MySQL)不支持
UNBOUNDED FOLLOWING,此时应换用FIRST_VALUE配合降序排序
FIRST_VALUE + 降序比 LAST_VALUE 更安全
多数人想取“最新一条”,本质是“按时间倒序后的第一条”。FIRST_VALUE(col) OVER (PARTITION BY g ORDER BY updated_at DESC) 天然匹配该语义,且默认帧就足够——不用额外写 UNBOUNDED FOLLOWING,也不依赖对 LAST_VALUE 帧规则的记忆。
- 排序字段重复时,务必加二级排序,例如:
ORDER BY updated_at DESC, id DESC - 若字段可能为
NULL,建议显式写NULLS LAST(PostgreSQL)或避免NULL参与排序(MySQL) -
LAST_VALUE不能直接取整行,别硬套;它逐列计算,LAST_VALUE(x)和LAST_VALUE(y)可能来自不同物理行
真正要取最新完整记录,优先用 ROW_NUMBER()
LAST_VALUE 解决不了“取最新订单的 id + amount + status”这种需求。它只适合辅助场景,比如广播“每组最大时间”到所有行,再配合 JOIN 或 WHERE 匹配。
- 正确路径:
ROW_NUMBER() OVER (PARTITION BY g ORDER BY updated_at DESC, id DESC) AS rn,外层WHERE rn = 1 - 大表上若
ORDER BY列无索引,OVER计算开销会陡增 - 复杂点在于:窗口函数的行为不只取决于
ORDER BY,还强依赖帧定义;而“最后一条记录”这个业务目标,往往被误认为只需一个函数就能解决——实际上它常需要排序确定性、去重兜底、以及行级筛










