Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > MySQL查询优化

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

作者:detayun

在数据分析 或业务查询中,我们经常需要 按某个字段分组,并取每组中时间最新的记录,下面我们就来看看MySQL高效获取每个分组的最新记录的具体方法吧

引言

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

但 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 为什么它能工作?

内层查询:

外层 GROUP BY:

2.3 问题与风险

依赖 MySQL 的 GROUP BY 行为:

性能问题:

代码可读性差:

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;

优点:

方案 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. 总结

最终推荐 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查询优化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文