SQL如何获取分组中第一条或最后一条记录_利用FIRST_VALUE函数
SQL如何获取分组中第一条或最后一条记录:利用FIRST_VALUE函数

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈
为什么FIRST_VALUE返回的不是“每组第一条记录”?
很多朋友第一次用FIRST_VALUE窗口函数时,都会遇到一个困惑:结果怎么和想的不一样?它确实按你指定的顺序,取到了窗口里的第一个值,但问题是——它把那个值“复制”给了组里的每一行,行数一点没少。这就像给团队每个人都发了一份队长的名片,但队长本人并没有被单独请出来。而我们实际业务中想要的,往往是“每组只留一条代表记录”,比如最新的那笔订单、最早的那次登录、分数最高的那位学生。这时候你就会发现,FIRST_VALUE更像是个辅助工具,单靠它,解决不了“提取整行数据”这个核心问题。
怎么用FIRST_VALUE配合ROW_NUMBER拿到完整记录?
那么,正确的“组合拳”该怎么打呢?核心思路其实很清晰:分两步走。第一步,用ROW_NUMBER()给每个分组内的行,按照你的排序规则(比如时间倒序)编上号(1,2,3…)。第二步,在外层查询里,轻松地WHERE rn = 1,只把每组的“1号”选手筛选出来。在这个过程中,FIRST_VALUE可以作为一个验证字段出现,但它不是筛选的主力。
这里有三个关键点必须注意:
- 排序是灵魂:
ORDER BY子句必须明确写在OVER()里面,否则ROW_NUMBER的编号顺序就是不确定的,结果自然不可靠。 - 分组要对齐:
PARTITION BY里的字段(比如user_id),必须和你想分组的逻辑完全一致。 - “最后一条”怎么取:很简单,把
ORDER BY created_at DESC改成ASC就行了。当然,你也可以用LAST_VALUE,但要小心它的默认窗口范围,最好显式指定为RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING,以免出错。
来看一个经典例子(获取每个用户的最新订单):
SELECT order_id, user_id, amount, created_at
FROM (
SELECT order_id, user_id, amount, created_at,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn
FROM orders
) t
WHERE rn = 1;
FIRST_VALUE在SELECT里直接用有哪些陷阱?
如果把FIRST_VALUE当成“取一行”的函数来用,很容易踩坑。因为它本质上是个“填充”函数,不负责删减行数。下面这几种情况,就是典型的误用:
- 场景一:想找“每组金额最高的订单号”。如果直接写
FIRST_VALUE(order_id) OVER (PARTITION BY user_id ORDER BY amount DESC),结果会是每一行都显示同一个最高的订单号,但你却看不到这个订单对应的金额、时间等其他信息。这显然不是我们想要的完整记录。 - 场景二:忘了写
ORDER BY。这时数据库可能会按物理存储顺序来排,每次执行的结果都可能不同,毫无稳定性可言。 - 场景三:在聚合查询里混用。窗口函数和
GROUP BY聚合不能直接放在同一层SELECT中,否则要么报错,要么语义变得混乱不清。
所以,更稳妥的做法是:要么先用子查询或CTE把窗口计算的结果准备好,再去做关联查询;要么就坚持使用ROW_NUMBER配合过滤的方案,思路直观,控制力也强。
MySQL 8.0+ 和 PostgreSQL 的行为差异要点
虽然主流数据库都支持这些窗口函数,但魔鬼藏在细节里,跨平台使用时尤其要注意:
- MySQL 8.0+ 更“较真”:它对
ORDER BY子句里的表达式要求更严格。比如,你不能直接使用SELECT列表里定义的别名(像ORDER BY dt),必须写原始字段名或列位置。 - PostgreSQL 处理空值更灵活:它支持
ORDER BY ... NULLS FIRST/LAST语法,让你能明确控制空值排在前面还是后面,这在MySQL里目前还不行。 - SQL Server 有“捷径”:在某些简单场景下,使用
TOP 1 WITH TIES配合FETCH NEXT子句,写起来可能比窗口函数更简短。但这种方法不具备跨多个分组分别取第一条的能力,功能上还是有区别的。
总的来说,如果考虑代码的兼容性和可读性,ROW_NUMBER方案是更安全的选择。而FIRST_VALUE,更适合扮演一个“锦上添花”的角色,用来生成一些派生字段,不建议让它承担核心的数据筛选逻辑。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
mysql如何限制单条SQL执行消耗的内存_调整sort_buffer_size与join_buffer
MySQL内存调优实战:如何精准控制单条SQL的内存消耗? 说到MySQL性能调优,sort_buffer_size和join_buffer_size这两个参数总是绕不开的话题。很多工程师的第一反应是:“调大点是不是就能快些?” 事情可没这么简单。盲目调整不仅可能毫无收益,甚至还会引发内存溢出(OO
Redis发布订阅支持消息类型自定义吗_通过序列化与反序列化规范消息结构
Redis发布订阅不校验消息类型,业务需自行约定序列化协议 简单来说,Redis的发布订阅(Pub Sub)机制本身,对消息内容是完全“无感”的。它就像一个只管搬运、不管验货的传送带。这意味着,消息类型的定义、校验和解析,完全落在了业务开发者的肩上。在Spring Boot这类框架中,如果使用不当,
SQL如何计算分组内的方差与标准差_窗口聚合函数实操
SQL中VARIANCE和STDDEV默认按样本计算(除以n-1),PostgreSQL、Oracle、Snowflake均如此;MySQL的VARIANCE()等价VAR_SAMP(),STDDEV()等价STDDEV_SAMP();SQL Server需显式用STDEV()或STDEVP()。
为什么SQL触发器在执行存储过程时不触发_排查触发器嵌套触发限制
为什么SQL触发器在执行存储过程时不触发?排查触发器嵌套触发限制 触发器调用存储过程后不触发,根本不是“不触发”,而是被嵌套层数限制拦住了 很多开发者遇到触发器“失灵”时,第一反应是检查语法或权限。但真相往往更直接:你很可能撞上了SQL Server那堵硬性的32层嵌套墙。无论是DML还是DDL触发
mysql如何高效地统计不同状态的数量_使用CountIf单次扫描
MySQL不支持COUNTIF函数,需用SUM(CASE WHEN THEN 1 ELSE 0 END)实现单次扫描多状态统计,比多次COUNT(*)更高效。 MySQL 没有 COUNTIF 函数,别白找 如果你是从Excel或者其他数据库(比如SQLite、PostgreSQL)转过来的,可
- 日榜
- 周榜
- 月榜
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
相关攻略
2015-03-10 11:25
2015-03-10 11:05
2021-08-04 13:30
2015-03-10 11:22
2015-03-10 12:39
2022-05-16 18:57
2025-05-23 13:43
2025-05-23 14:01
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程
热门话题

