LAST_VALUE默认不返回组内最后一条记录,因其窗口帧为ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW;需显式指定ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING,并配合正确ORDER BY,或改用FIRST_VALUE+逆序更安全。

LAST_VALUE 默认不返回组内最后一条记录
直接用 LAST_VALUE 窗口函数却拿不到组内末条数据,是初学者最常踩的坑。根本原因是它默认的窗口帧(frame)是 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,也就是说只看当前行及之前的数据——所以无论怎么排序,LAST_VALUE 在每一行都只返回“到当前位置为止”的最后一个值,而不是整个分组的最后一条。
必须显式指定窗口帧为整个分组
要让 LAST_VALUE 真正取到组内末条记录,得把窗口范围拉满到整组:用 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING。同时注意排序逻辑必须和业务意图一致,否则“末条”可能不是你想要的那条。
常见写法示例:
SELECT
id, category, amount,
LAST_VALUE(amount) OVER (
PARTITION BY category
ORDER BY created_at
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS last_amount_in_category
FROM orders;
- 漏写
ROWS BETWEEN ...就会掉进默认帧陷阱 -
ORDER BY必须存在,否则语法报错(即使你只关心分组末尾,SQL 标准仍要求指定排序依据) - 如果
created_at有重复,末条可能不确定;必要时加二级排序,如ORDER BY created_at, id
替代方案:用 FIRST_VALUE 配合倒序更安全
比起硬调 LAST_VALUE,多数人更习惯用 FIRST_VALUE + 逆序排序来等价实现,语义更直观、兼容性更好(比如在某些旧版 PostgreSQL 或 Hive 中 LAST_VALUE 行为不一致)。
等效写法:
SELECT
id, category, amount,
FIRST_VALUE(amount) OVER (
PARTITION BY category
ORDER BY created_at DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS last_amount_in_category
FROM orders;
- 不用记
LAST_VALUE的帧陷阱,靠ORDER BY ... DESC把“第一”变成“实际最后” - 所有主流 SQL 引擎对
FIRST_VALUE的实现更统一 - 如果只需要末条记录的完整行(不止一个字段),建议改用
ROW_NUMBER()+ 子查询,避免多次窗口计算开销
真正要取整条末记录时,LAST_VALUE 不是最佳选择
当目标是“取出每个分组中时间最新的那一条完整记录”,用 LAST_VALUE 只能逐字段取值,无法原子性地拿到对应行的其他列(比如最新订单的 id 和 status)。这时容易写出错误关联或重复计算。
推荐做法是用 ROW_NUMBER() 标记每组内的顺序:
SELECT id, category, amount, created_at
FROM (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY category
ORDER BY created_at DESC, id DESC
) AS rn
FROM orders
) t
WHERE rn = 1;
-
LAST_VALUE是标量函数,天生不适合“取整行”场景 -
ROW_NUMBER()方案逻辑清晰,可读性强,且能稳定处理并列情况(通过二级排序) - 如果表很大,记得在
(category, created_at)上建联合索引,否则ORDER BY ... PARTITION BY可能很慢
窗口函数的“最后一行”从来不是单靠名字就能猜对的行为,关键在帧定义和排序意图是否真正对齐。别被函数名带偏节奏。

















