MySQL查询优化之高效获取每个分组的最新记录

 更新时间:2026年03月02日 17:14:35   作者:detayun  
在数据分析 或业务查询中,我们经常需要 按某个字段分组,并取每组中时间最新的记录,下面我们就来看看MySQL高效获取每个分组的最新记录的具体方法吧

引言

在数据分析 或业务查询中,我们经常需要 “按某个字段分组,并取每组中时间最新的记录”。例如:

  • 按 data_time 分组,取每组 insert_time 最大的记录。
  • 按用户 ID 分组,取每个用户的最新订单。

但 MySQL 的 GROUP BY 默认行为可能无法直接满足需求,尤其是当查询依赖 ORDER BY 时。本文将深入探讨 一种常见的 hack 方案(依赖 LIMIT),并分析其问题,最终给出 更高效、更标准的解决方案。

1. 常见问题:如何获取每个分组的最新记录?

假设我们有一个表 spider.nbs_data,结构如下:

CREATE TABLE spider.nbs_data (
    id BIGINT PRIMARY KEY,
    index_id VARCHAR(20),
    data_time DATE,
    insert_time DATETIME,
    -- 其他字段...
);

需求:查询 index_id = '0000120460' 且 data_time >= '2020-12-31' 的数据,按 data_time 分组,并取每组 insert_time 最大的记录。

2. 一种常见的 hack 方案:依赖 LIMIT 的 GROUP BY

2.1 原始 SQL

SELECT * FROM (
    SELECT * 
    FROM spider.nbs_data 
    WHERE index_id = '0000120460' AND data_time >= '2020-12-31' 
    ORDER BY `insert_time` DESC 
    LIMIT 10000000000  -- 超大 LIMIT 值
) t 
GROUP BY t.data_time  -- 按 data_time 分组
ORDER BY `data_time`;

2.2 为什么它能工作?

内层查询:

  • 先筛选符合条件的数据。
  • 按 insert_time DESC 排序,确保最新记录排在前面。
  • LIMIT 10000000000 强制 MySQL 先执行 ORDER BY 再 GROUP BY,避免优化器打乱顺序。

外层 GROUP BY:

  • MySQL 默认取每组的第一条记录(即 insert_time 最大的那条)。
  • 最终按 data_time 排序输出。

2.3 问题与风险

依赖 MySQL 的 GROUP BY 行为:

  • 如果 SELECT * 包含非分组列,可能触发 ONLY_FULL_GROUP_BY 错误(MySQL 5.7+ 默认启用)。
  • 不同 MySQL 版本或 SQL 模式可能导致结果不一致。

性能问题:

  • LIMIT 10000000000 相当于 全表扫描,数据量大时性能极差。
  • 如果数据量超过 LIMIT 值,可能丢失数据。

代码可读性差:

LIMIT 10000000000 是一个 hack 行为,容易让后续维护者困惑。

3. 更优方案:标准 SQL 实现

方案 1:JOIN + 子查询(兼容所有 MySQL 版本)

SELECT t1.*
FROM spider.nbs_data t1
JOIN (
    SELECT data_time, MAX(insert_time) AS max_insert_time
    FROM spider.nbs_data
    WHERE index_id = '0000120460' AND data_time >= '2020-12-31'
    GROUP BY data_time
) t2 ON t1.data_time = t2.data_time AND t1.insert_time = t2.max_insert_time
WHERE t1.index_id = '0000120460'
ORDER BY t1.data_time;

优点:

  • 清晰、标准,不依赖 GROUP BY 行为。
  • 性能较好(子查询先聚合,再 JOIN)。
  • 兼容所有 MySQL 版本。

方案 2:ROW_NUMBER()(MySQL 8.0+)

WITH ranked_data AS (
    SELECT *,
           ROW_NUMBER() OVER (PARTITION BY data_time ORDER BY insert_time DESC) AS rn
    FROM spider.nbs_data
    WHERE index_id = '0000120460' AND data_time >= '2020-12-31'
)
SELECT * FROM ranked_data WHERE rn = 1 ORDER BY data_time;

优点:

  • 语法简洁,逻辑清晰。
  • 性能优秀(窗口函数优化较好)。
  • 适用于复杂分组排序场景。

缺点:

仅 MySQL 8.0+ 支持。

4. 性能对比

方案适用场景性能兼容性
LIMIT hack快速验证、临时查询差(全表扫描)所有版本
JOIN + 子查询所有 MySQL 版本所有版本
ROW_NUMBER()MySQL 8.0+8.0+

5. 总结

  • 避免依赖 LIMIT 的 hack 方案:虽然能工作,但性能差、可读性低、存在风险。
  • 推荐 JOIN + 子查询:兼容性好,性能稳定,适合大多数场景。
  • MySQL 8.0+ 优先用 ROW_NUMBER():语法简洁,性能更优。

最终推荐 SQL

-- MySQL 5.7 及以下版本
SELECT t1.*
FROM spider.nbs_data t1
JOIN (
    SELECT data_time, MAX(insert_time) AS max_insert_time
    FROM spider.nbs_data
    WHERE index_id = '0000120460' AND data_time >= '2020-12-31'
    GROUP BY data_time
) t2 ON t1.data_time = t2.data_time AND t1.insert_time = t2.max_insert_time
WHERE t1.index_id = '0000120460'
ORDER BY t1.data_time;

-- MySQL 8.0+
WITH ranked_data AS (
    SELECT *,
           ROW_NUMBER() OVER (PARTITION BY data_time ORDER BY insert_time DESC) AS rn
    FROM spider.nbs_data
    WHERE index_id = '0000120460' AND data_time >= '2020-12-31'
)
SELECT * FROM ranked_data WHERE rn = 1 ORDER BY data_time;

结语

在 SQL 查询中,清晰、标准、高效 是最重要的原则。依赖 LIMIT 的 hack 方案虽然能解决一时之需,但长期来看,使用 JOIN 或窗口函数才是更可靠的选择。希望本文能帮助你优化查询,写出更健壮的 SQL

到此这篇关于MySQL查询优化之高效获取每个分组的最新记录的文章就介绍到这了,更多相关MySQL查询优化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MySQL服务自动停止的解决方法

    MySQL服务自动停止的解决方法

    这篇文章主要给大家介绍了MySQL服务自动停止的解决方法,文中给出了详细的解决过程,对大家具有一定的参考学习价值,需要的朋友们下面来一起看看吧。
    2017-06-06
  • 详解MySQL中DELETE NOT IN删除的常见问题与解决方案

    详解MySQL中DELETE NOT IN删除的常见问题与解决方案

    在数据库操作中,​​DELETE​​ 语句用于从表中删除数据,当需要根据某些条件进行删除时,​​NOT IN​​ 子句是一个常用的条件表达式,本文将探讨如何在 MySQL 中使用 ​​DELETE ... NOT IN​​ 语句,并讨论一些常见的问题及其解决方案,需要的可以参考下
    2025-10-10
  • MySQL生成日期维度表的sql语句

    MySQL生成日期维度表的sql语句

    这篇文章主要介绍了MySQL生成日期维度表的sql语句,通过存储过程生成,其次是通过递归的公用表表达式生成,需要的朋友可以参考下
    2024-07-07
  • 一文深入探究MySQL自增锁

    一文深入探究MySQL自增锁

    MySQL的自增锁是指在使用自增主键(Auto Increment)时,为了保证唯一性和正确性,系统会对自增字段进行加锁,这样可以确保同时插入多条记录时,每条记录都能够获得唯一的自增值,本将和大家一起深入探究MySQL自增锁,需要的朋友可以参考下
    2023-08-08
  • mysql连接错误2013的问题及解决

    mysql连接错误2013的问题及解决

    这篇文章主要介绍了mysql连接错误2013的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • MySQL中TEXT类型存储极限与实践案例

    MySQL中TEXT类型存储极限与实践案例

    本文解析MySQL TEXT类型存储极限及工程实践,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2022-10-10
  • MySQL性能优化神器Explain的基本使用分析

    MySQL性能优化神器Explain的基本使用分析

    这篇文章主要给大家介绍了关于MySQL性能优化神器Explain的基本使用分析,文中通过示例代码介绍的非常详细,对大家学习或者使用MySQL具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-08-08
  • mysql sharding(碎片)介绍

    mysql sharding(碎片)介绍

    这篇文章主要介绍了mysql sharding(碎片)介绍,本文讲解了Sharding的应用场景一般都哪些、Sharding与数据库分区(Partition)的区别等内容,需要的朋友可以参考下
    2015-03-03
  • mysql 模糊查询 concat()的用法详解

    mysql 模糊查询 concat()的用法详解

    大家都知道concat()函数,是用来连接字符串,今天通过本文给大家介绍mysql 模糊查询 concat()及concat的用法,感兴趣的朋友跟随小编一起看看吧
    2023-02-02
  • MySQL慢查询以及解决方案详解

    MySQL慢查询以及解决方案详解

    MySQL的慢查询,全名是慢查询日志,是MySQL提供的一种日志记录,用来记录在MySQL中响应时间超过阀值的语句,下面这篇文章主要给大家介绍了关于MySQL慢查询以及解决方案的相关资料,需要的朋友可以参考下
    2023-05-05

最新评论