LAST_VALUE默认只返回当前行而非分组最后一条,因默认窗口帧为ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW;需显式指定UNBOUNDED FOLLOWING并配合确定性ORDER BY(如时间降序+二级排序)才能正确取最新值。

LAST_VALUE 默认是 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
这是最常踩的坑:直接写 LAST_VALUE(col) 却发现返回的不是分组内最后一条,而是当前行——因为窗口默认帧(frame)只包含从开头到当前行,LAST_VALUE 在这个范围内当然取的是“当前行”。它不是“整个分组最后一条”,而是“当前滑动窗口里最后一个”。
实操建议:
- 必须显式指定窗口帧为
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING,才能覆盖整组 - 同时确保
ORDER BY逻辑能定义出明确的“最后”顺序(比如按时间戳降序,再取LAST_VALUE才有意义) - 如果想按业务时间取最新记录,但时间有重复,得加二级排序(如
ORDER BY create_time DESC, id DESC),否则结果不确定
用 LAST_VALUE 还是 FIRST_VALUE + ORDER BY 降序更稳妥?
很多场景下,要拿分组最新一条数据,其实用 FIRST_VALUE 更直观、更少出错。因为只要把排序改成降序,FIRST_VALUE 天然就对应“最新”。
实操建议:
- 写
FIRST_VALUE(col) OVER (PARTITION BY group_id ORDER BY update_time DESC)比LAST_VALUE更不容易漏掉帧定义 -
LAST_VALUE在 PostgreSQL 和 SQL Server 中行为一致,但在某些旧版 MySQL(8.0 前)不支持窗口函数,别误用 - 如果后续要过滤出每组唯一一行(不是在原表上加一列),
FIRST_VALUE仍需配合ROW_NUMBER(),不能只靠它
真正要“获取分组最后一条记录整行”,LAST_VALUE 不够用
LAST_VALUE 只能逐列取值,没法直接返回整行。你无法用它一次性拿到 id、name、status 等多个字段的“最后一条组合”。强行套用会导致多列 LAST_VALUE 各自计算,可能来自不同行(尤其当排序字段不唯一时)。
实操建议:
- 正确做法是先用
ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ... DESC)标记每组序号,再WHERE rn = 1 - 若性能敏感且数据量大,注意
ORDER BY字段要有索引,特别是PARTITION BY和排序字段的联合索引 - MySQL 8.0+、PostgreSQL、SQL Server 都支持,但 SQLite 3.25+ 才开始支持窗口函数,上线前务必确认版本
ORDER BY NULL 或没写 ORDER BY 会触发隐式错误
有些方言(如 MySQL 8.0)允许窗口函数不写 ORDER BY,但这时 LAST_VALUE 行为未定义,可能返回任意行;而 PostgreSQL 则直接报错 window function requires an ORDER BY clause。
实操建议:
- 永远显式写
ORDER BY,哪怕业务上觉得“顺序无所谓”,也要写个确定字段(如ORDER BY id)避免歧义 - 不要依赖
ORDER BY NULL来绕过排序开销——这在多数引擎中不会跳过排序,反而可能引发兼容性问题 - 测试时用小数据集验证结果是否稳定,尤其检查分组内多条记录时间相同时的输出是否符合预期

















