如何用窗口函数 FIRST_VALUE / LAST_VALUE 取分组首尾值
发布时间 - 2026-01-28 00:00:00 点击率:次根本原因是FIRST_VALUE和LAST_VALUE默认窗口帧为ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,导致LAST_VALUE仅取当前行及之前行的末值而非全分组末值;需显式指定UNBOUNDED FOLLOWING并确保ORDER BY唯一确定、妥善处理NULL。
为什么 FIRST_VALUE 和 LAST_VALUE 有时取不到预期的首尾值?
根本原因是这两个函数默认的窗口帧(frame)是 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,也就是说,LAST_VALUE 并不是看整个分组的最后一条,而是看“当前行及之前所有行”里的最后一个——这通常不是你想要的。而 FIRST_VALUE 虽然在此帧下总能取到分组第一行(因起始是 UNBOUNDED PRECEDING),但它的行为也依赖于 ORDER BY 是否明确、是否唯一。
常见错误现象:LAST_VALUE(col) 返回的总是当前行的值,或某几行重复出现同一个“末尾值”,分组内其他行没更新。
- 必须显式指定窗口帧为
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING -
ORDER BY子句不可省略——即使业务上不关心顺序,也得选一个确定性排序列(如主键、时间戳) - 若
ORDER BY列存在重复值,且未加额外去重手段(如ORDER BY ts, id),FIRST_VALUE/LAST_VALUE的结果可能非确定
PostgreSQL / MySQL 8.0+ / SQL Server 中正确写法示例
以按用户分组、取每个用户最早和最晚订单金额为例:
SELECT
user_id,
order_time,
amount,
FIRST_VALUE(amount) OVER (
PARTITION BY user_id
ORDER BY order_time, order_id
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS first_amount,
LAST_VALUE(amount) OVER (
PARTITION BY user_id
ORDER BY order_time, order_id
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS last_amount
FROM orders;
注意点:
-
ORDER BY order_time, order_id确保排序唯一,避免并列导致FIRST_VALUE随机选中某一行 - MySQL 8.0+ 和 PostgreSQL 支持该完整语法;SQL Server 同样支持,但旧版本(如 2012)的
LAST_VALU帧必须显式声明,否则行为异常
E
- SQLite 目前(3.45)仍不支持自定义帧,
LAST_VALUE无法可靠使用
替代方案:用 MIN/MAX 还是 FIRST_VALUE/LAST_VALUE?
如果只是取分组内某个字段的极值(比如最大金额、最小时间),直接用 MIN()/MAX() 更简洁安全;但如果你需要的是「对应那条记录的其他字段」,就必须用窗口函数。
例如:要取每个用户「最早订单的订单号 + 金额 + 商品名」,就不能只靠 MIN(order_time),因为没法把商品名一起捞出来——这时得靠 FIRST_VALUE(order_id)、FIRST_VALUE(amount)、FIRST_VALUE(product_name) 同步拉取。
-
MIN/MAX是聚合函数,丢失行上下文;FIRST_VALUE/LAST_VALUE是窗口函数,保留原始行结构 - 性能上,带完整帧的
LAST_VALUE在大数据集可能比MAX略慢,因需维护完整窗口状态 - 某些引擎(如 BigQuery)对
LAST_VALUE的帧优化较好,但 Hive/Spark SQL 旧版本可能有 bug,建议测试验证
容易被忽略的 NULL 处理与排序方向
FIRST_VALUE 和 LAST_VALUE 默认把 NULL 当作最小值(即 ORDER BY col ASC 时 NULL 排最前)。如果你的排序列含 NULL,且希望它们被排除在首尾之外,得提前过滤或用 CASE 调整顺序。
- 安全做法:在
ORDER BY中写ORDER BY col IS NULL, col把NULL排最后 - 或者用
COALESCE(col, '9999-12-31')(日期场景)临时替换,但要注意类型兼容性 - 反向取“最后非 NULL 值”时,别只改
DESC,还要配合NULLS LAST(PostgreSQL/Oracle 支持),否则LAST_VALUE可能真就取到NULL
真正麻烦的从来不是语法怎么写,而是排序依据是否稳定、NULL 怎么归置、以及不同数据库对 ROWS BETWEEN 的实际实现差异——这些地方一错,首尾值就静悄悄地偏了。
# mysql
# oracle
# 大数据
# win
# 聚合函数
# 为什么
# sql
# NULL
# sqlite
# hive
# spark
# postgresql
# 数据库
# bug
# 根本原因
# 的是
# 旧版本
# 如果你
# 子句
# 在此
# 这两个
# 较好
# 能有
# 就不能
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel广播系统如何实现实时通信_Laravel Reverb与WebSockets实战教程
php485函数参数是什么意思_php485各参数详细说明【介绍】
新三国志曹操传主线渭水交兵攻略
北京网站制作的公司有哪些,北京白云观官方网站?
Laravel如何实现数据库事务?(DB Facade示例)
,怎么在广州志愿者网站注册?
java中使用zxing批量生成二维码立牌
利用 Google AI 进行 YouTube 视频 SEO 描述优化
如何撰写建站申请书?关键要点有哪些?
Laravel如何实现用户角色和权限系统_Laravel角色权限管理机制
C++时间戳转换成日期时间的步骤和示例代码
Laravel怎么实现观察者模式Observer_Laravel模型事件监听与解耦开发【指南】
高端网站建设与定制开发一站式解决方案 中企动力
武汉网站设计制作公司,武汉有哪些比较大的同城网站或论坛,就是里面都是武汉人的?
怎样使用JSON进行数据交换_它有什么限制
WordPress 子目录安装中正确处理脚本路径的完整指南
Laravel如何理解并使用服务容器(Service Container)_Laravel依赖注入与容器绑定说明
C++用Dijkstra(迪杰斯特拉)算法求最短路径
美食网站链接制作教程视频,哪个教做美食的网站比较专业点?
html5怎么画眼睛_HT5用Canvas或SVG画眼球瞳孔加JS控制动态【绘制】
魔方云NAT建站如何实现端口转发?
如何生成腾讯云建站专用兑换码?
Laravel如何实现一对一模型关联?(Eloquent示例)
如何制作新型网站程序文件,新型止水鱼鳞网要拆除吗?
公司网站制作价格怎么算,公司办个官网需要多少钱?
如何在Windows环境下新建FTP站点并设置权限?
宙斯浏览器怎么屏蔽图片浏览 节省手机流量使用设置方法
EditPlus中的正则表达式实战(6)
Laravel怎么集成Log日志记录_Laravel单文件与每日日志配置及自定义通道【详解】
Linux系统运维自动化项目教程_Ansible批量管理实战
利用vue写todolist单页应用
高端建站如何打造兼具美学与转化的品牌官网?
详解Nginx + Tomcat 反向代理 如何在高效的在一台服务器部署多个站点
网站建设保证美观性,需要考虑的几点问题!
奇安信“盘古石”团队突破 iOS 26.1 提权
Win11怎么设置虚拟桌面 Win11新建多桌面切换操作【技巧】
悟空识字怎么关闭自动续费_悟空识字取消会员自动扣费步骤
清除minerd进程的简单方法
图册素材网站设计制作软件,图册的导出方式有几种?
使用Dockerfile构建java web环境
Laravel怎么集成Vue.js_Laravel Mix配置Vue开发环境
Laravel集合Collection怎么用_Laravel集合常用函数详解
如何在建站主机中优化服务器配置?
Win11应用商店下载慢怎么办 Win11更改DNS提速下载【修复】
jQuery中的100个技巧汇总
如何在万网主机上快速搭建网站?
Python结构化数据采集_字段抽取解析【教程】
android nfc常用标签读取总结
php 三元运算符实例详细介绍
Laravel如何使用Service Container和依赖注入?(代码示例)


