当前位置:首页 > 文章列表 > 数据库 > MySQL > MySQL 窗口函数按分组取每组最新记录的实现方法

MySQL 窗口函数按分组取每组最新记录的实现方法

来源:17golang原创 2026-09-15 20:06:46 0浏览 收藏

我在整理订单状态报表时,最容易写错的不是“按时间倒序”,而是“每组只留一条”这一句。直接给每个客户排序并不能自动减少结果行,GROUP BY 也会把明细列的选择变得含糊。更稳的做法是先用 ROW_NUMBER() 在每个分组内编号,再由外层查询筛出 rn = 1

要点速览
  • PARTITION BY 决定“每组”的边界,ORDER BY 决定最新记录的顺序。
  • 时间相同不能只依赖数据库的自然顺序,应追加唯一键作为稳定的第二排序条件。
  • 窗口函数先产生编号,外层查询再过滤;过滤条件和索引要放在正确的数据边界上。

先把“每组最新”拆成三个字段

下面假设表 order_status_events 保存订单状态事件,customer_id 是分组键,updated_at 表示事件时间,event_id 是唯一递增标识。实际业务中,分组键也可能是门店、设备或项目,关键是先说清楚一组到底代表什么。

问题对应 SQL 位置判断标准
按谁分组PARTITION BY customer_id同一客户的事件进入同一窗口
什么叫最新ORDER BY updated_at DESC时间越晚排名越靠前
时间相同怎么办event_id DESC用唯一键固定最终选择
MySQL ROW_NUMBER 按 customer_id 分组并按 updated_at 和 event_id 排序的查询结构说明图
图1:MySQL 窗口分组与稳定排序的结构说明图,展示分组键、排序键和行号的静态关系,不是运行截图。

用 ROW_NUMBER() 给每个分组编号

窗口函数不会像 GROUP BY 那样把一组折叠成一行,而是为每条明细计算一个结果。MySQL 官方语法中,PARTITION BY 划分窗口,窗口内的 ORDER BY 负责排序;因此可以把“取最新”写成下面的 CTE。

WITH ranked_events AS (
    SELECT
        customer_id,
        order_id,
        status,
        updated_at,
        event_id,
        ROW_NUMBER() OVER (
            PARTITION BY customer_id
            ORDER BY updated_at DESC, event_id DESC
        ) AS rn -- 先按时间,再用唯一键稳定打破并列
    FROM order_status_events
)
SELECT
    customer_id,
    order_id,
    status,
    updated_at
FROM ranked_events
WHERE rn = 1 -- 外层过滤每组的第一名
ORDER BY customer_id;

这里的 rn = 1 不是在同一层窗口表达式旁边直接过滤,而是放在外层查询。这样写也方便后续把 rn 改成“每组最新三条”。

时间相同仍要给出确定结果

只写 ORDER BY updated_at DESC 时,如果同一客户有两条事件的时间完全相同,它们在窗口排序中属于并列记录,哪一条得到第一名就不再是明确的业务规则。生产查询应追加能唯一确定顺序的字段,例如 event_id、雪花 ID 或可靠的写入序号。

如果业务口径是“每个订单最新状态”,窗口应改为 PARTITION BY order_id;如果是“每个客户每种状态各取一条”,则要写成 PARTITION BY customer_id, status。这不是换一个字段的小修饰,而是在改变结果的粒度,最好先用三四组样例数据核对。

MySQL 窗口查询从明细行到 rn 等于 1 结果集的边界关系说明图
图2:从明细事件到 rn = 1 结果集的边界说明图,强调外层筛选、并列时间和不同分组粒度的关系,不是运行截图。

过滤范围、索引和复核清单

如果只查最近 30 天的事件,应把时间条件放进 CTE,让窗口函数只对目标范围编号:

WITH ranked_events AS (
    SELECT
        customer_id, order_id, status, updated_at, event_id,
        ROW_NUMBER() OVER (
            PARTITION BY customer_id
            ORDER BY updated_at DESC, event_id DESC
        ) AS rn -- 窗口只处理已经过滤的数据
    FROM order_status_events
    WHERE updated_at >= '2026-08-16 00:00:00' -- 示例时间边界
)
SELECT customer_id, order_id, status, updated_at
FROM ranked_events
WHERE rn = 1;

索引可以从过滤列、分组列和排序列的组合开始评估,例如 (updated_at, customer_id, event_id) 或按实际过滤方式调整;窗口排序是否仍需要额外工作,要以 EXPLAIN 的访问路径和数据规模为准,不能只凭索引名字判断。

  • 核对分组列是否真的是业务口径,而不是误把 order_id 当成客户分组。
  • 给时间相同的样例补上唯一键,确认重复执行得到同一条记录。
  • 先限定数据范围再编号,避免把历史全量数据带进当前报表。

常见问题

为什么不能直接 GROUP BY customer_id 取 updated_at?

MAX(updated_at) 只能得到最大时间,不能自动把同一行的 order_idstatus 一起带出来。先编号再取第一名,才能保留完整明细。

RANK() 能不能替代 ROW_NUMBER()?

如果并列时间需要全部保留,可以考虑 RANK();如果要求每组严格一行,就使用 ROW_NUMBER() 并补充唯一排序键。

窗口函数是不是一定比相关子查询快?

不能一概而论。窗口写法更直接,但最终仍受过滤范围、数据量、索引和排序代价影响,应结合实际数据用 EXPLAIN 比较。

这类查询的核心不是记住一段固定 SQL,而是先确定分组粒度,再用完整排序条件把“最新”定义成一个确定结果。只要 PARTITION BY、排序键和外层 rn = 1 三处口径一致,后续扩展到每组 Top N 也只是调整筛选条件。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go io限制上传读取长度的安全写法Go io限制上传读取长度的安全写法
上一篇
Go io限制上传读取长度的安全写法
Go HTTP 超时把 HeaderTimeout 与整体超时分开的配置方法
下一篇
Go HTTP 超时把 HeaderTimeout 与整体超时分开的配置方法
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    43次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    138次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    74次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    39次使用
  • CMMLU中文大模型评估基准:功能、使用教程与应用场景解析
    CMMLU
    深入了解CMMLU中文评估基准,涵盖67个学科主题,提供数据集下载、Zero-shot/Five-shot评估方法及排行榜,助力优化中文语言模型性能。
    26次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码