LAST_VALUE默认仅返回当前行值,需显式指定ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING才能获取组内最后值;若需整条记录,应优先使用ROW_NUMBER()配合rn=1筛选。

LAST_VALUE 默认是取当前行的值,不是组内最后一条
很多人写 LAST_VALUE(col) OVER (PARTITION BY group_col ORDER BY time_col) 后发现结果全是当前行的值,甚至报错。这是因为 LAST_VALUE 的默认窗口帧是 ROWS BETWEEN CURRENT ROW AND CURRENT ROW,它只看自己这一行。必须显式指定窗口范围才能看到“组内最后一条”。
正确做法是加上 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING,让窗口覆盖整个分组:
SELECT
id,
group_id,
value,
LAST_VALUE(value) OVER (
PARTITION BY group_id
ORDER BY created_at
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS last_value_in_group
FROM records;- 不加
ROWS子句 → 默认只作用于当前行,LAST_VALUE等价于value -
UNBOUNDED FOLLOWING是关键,否则用CURRENT ROW结尾就看不到后面的行 - ORDER BY 必须明确且有确定性(比如带
id作为第二排序键),否则相同时间戳会导致结果不稳定
想取整条记录(不止一个字段)?LAST_VALUE 不够用
LAST_VALUE 只能逐字段取值,没法直接返回“最后那行的全部字段”。如果要获取完整记录(比如最后更新的用户信息、最新订单详情),硬套 LAST_VALUE 会写一堆重复的窗口函数,还容易因排序歧义出错。
更可靠的方式是用 ROW_NUMBER() 配合子查询或 CTE:
WITH ranked AS (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY updated_at DESC, id DESC
) AS rn
FROM orders
)
SELECT id, user_id, status, updated_at
FROM ranked
WHERE rn = 1;- 比多个
LAST_VALUE()更清晰、更易维护 -
ORDER BY ... DESC+rn = 1语义直白,不易误解 - 注意补全排序键(如
id DESC)避免并列第一时结果不确定
MySQL 8.0+ 和 PostgreSQL 行为一致,但旧版 MySQL 不支持
MySQL 在 8.0 之前没有窗口函数,强行用 LAST_VALUE 会报错 ERROR 1064。PostgreSQL 9.4+、SQL Server 2012+、Oracle 10g+ 均支持,但语法细节略有差异。
- PostgreSQL 允许
LAST_VALUE(col) IGNORE NULLS,MySQL 8.0 目前不支持IGNORE NULLS - SQL Server 中若未写
ROWS子句,LAST_VALUE默认行为仍是当前行 —— 和其他数据库一样,不能依赖“隐式全窗口” - SQLite 3.25+ 支持窗口函数,但
LAST_VALUE必须配ROWS,且不支持IGNORE NULLS
性能隐患:窗口帧太大会拖慢查询
用 UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING 意味着每个分组内每行都要扫描整个分区,数据量大时 I/O 和内存开销明显上升。特别是当 PARTITION BY 组数少、但每组行数极多(如单个用户几万条日志),这个窗口函数可能比等价的 ROW_NUMBER() + 过滤慢 2–3 倍。
- 如果只关心“最后一条”,优先用
ROW_NUMBER() ... DESC+WHERE rn = 1 - 如果真要所有行都带上最后值(比如做差值计算),再用
LAST_VALUE,并确认索引覆盖了PARTITION BY和ORDER BY字段 - 在 PostgreSQL 中可考虑用
DISTINCT ON替代(非标准 SQL,但高效):SELECT DISTINCT ON (group_id) * FROM t ORDER BY group_id, ts DESC
真正麻烦的不是语法怎么写,而是想清楚你要的是“最后的值”还是“最后那条记录”——这两个需求对应的实现路径、性能特征和边界行为完全不同。

















