MySQL 数据库 group by 语句怎么优化?

news2024/11/26 14:31:46

一、一个简单使用示例

我这里创建一张订单表

CREATE TABLE `order_info` (  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键',  `order_no` int NOT NULL COMMENT '订单号',  `goods_id` int NOT NULL DEFAULT '0' COMMENT '商品id',  `goods_name` varchar(50) NOT NULL COMMENT '商品名称',  `order_status` int NOT NULL DEFAULT '0' COMMENT '订单状态:1待支付,2成功支付,3支付失败,4已关闭',  `pay_type` int NOT NULL DEFAULT '0' COMMENT '支付方式:1微信支付,2支付宝支付',  `price` decimal(11,2) DEFAULT NULL COMMENT '订单金额',  `pay_time` datetime DEFAULT NULL COMMENT '支付时间',  PRIMARY KEY (`id`)) ENGINE=InnoDB  DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='订单信息表';

复制代码

同时也在表里插了一些数据

现在我们这里执行 group by 语句

select goods_name, count(*) as numfrom order_infogroup by goods_name

复制代码

很明显,这里就可以统计出来 每件商品一共有多少订单数据!

二、group by 原理分析

2.1、explain 分析

不同的数据库版本,用 explain 执行的结果并不一致,同样是上面 sql 语句

「MySQL 5.7 版本」

  • Extra 这个字段的Using temporary表示在执行分组的时候使用了临时表

  • Extra 这个字段的 Using filesort 表示使用了排序

「MySQL 8.0 版本」

我们通过对比可以发现:mysql 8.0 开始 group by 默认是没有排序的了!

接下来我们来解释下,什么是临时表。

2.2、聊一聊 Using temporary

Using temporary 表示由于排序没有走索引、使用union子查询连接查询,group_concat()count(distinct)表达式的求值等等会创建了一个内部临时表。

注意这里的临时表可能是内存上的临时表,也有可能是硬盘上的临时表,理所当然基于内存的临时表的时间消耗肯定要比基于硬盘的临时表的实际消耗小。

但不是说多大临时数据都可以直接存在内存的临时表,而是当超过最大内存临时表的最大容量就是转为存入磁盘临时表

当 mysql 需要创建临时表时,选择内存临时表还是硬盘临时表取决于参数tmp_table_sizemax_heap_table_size,当所需临时表的容量大于两者的最小值时,mysql 就会使用硬盘临时表存放数据。

用户可以在 mysql 的配置文件里修改该两个参数的值,两者的默认值均为 16M。

mysql> show global variables like 'max_heap_table_size';+---------------------+----------+| Variable_name       | Value    |+---------------------+----------+| max_heap_table_size | 16777216 |+---------------------+----------+1 row in set
mysql> show global variables like 'tmp_table_size';+----------------+----------+| Variable_name  | Value    |+----------------+----------+| tmp_table_size | 16777216 |+----------------+----------+1 row in set

复制代码

2.3、group by 是如何产生临时表的

同样以该 sql 分析

select goods_name, count(*) as numfrom order_infogroup by goods_name

复制代码

这个 SQL 产生临时表的执行流程如下

  1. 「创建内存临时表」,表里面有两个字段:goods_name 和 num;

  2. 全表扫描 order_info 表,取出 goods_name = 某商品(比如围巾、耳机、茶杯等)的记录

  3. 临时表没有 goods_name = 某商品的记录,直接插入,并记为 (某商品,1);

    临时表里有 goods_name = 某商品的记录,直接更新,把 num 值 +1

  4. 重复步骤 3 直至遍历完成,然后把结果集返回客户端。

这个流程的执行图如下:

三、group by 使用中注意的一个问题

我们来思考一个问题

select 的 列 和 group by 的 列 不一致会报错吗?

比如

 select goods_id, goods_name, count(*) as num from order_info group by goods_id;

复制代码

上面我们想根据商品 id 进行分组,统计每个商品的订单数量,但是我们分组只根据 goods_id 分组,但在查询列的时候,既要返回 goods_id,也要返回 goods_name。

我们这么写因为我们知道:一样的 goods_id 一定有相同的 goods_name,所以就没必要写成 group by goods_id,goods_name;

但上面这种写法一定会被支持吗?未必!

我们分别以 mysql5.7 版本和 8.0 版本做下尝试。

mysql5.7版本

我们发现是可以查询的到的。

mysql8.0版本

我们在执行上面 sql 发现报错了,没错同样的 sql 在不同的 mysql 版本执行结果并不一样,我们看下报什么错!

出现这个错误的原因是 mysql 的 sql_mode 开启了 ONLY_FULL_GROUP_BY 模式

该模式的含义就是: 对于 group by 聚合操作,如果在 select 中的列,没有在 group by 中出现,那么这个 sql 是不合法的,因为列不在 group by 从句中。

这其实是一种更加严谨的做法。

就比如上面这个 sql,如果存在这个商品的名称被修改过了,但是它们的 id 确还是一样的,那么这个时候展示的商品名称是修改前的还是修改后的呢?

那对于上面这种情况,mysql5.7 版本是如何做的呢?

1.「创建内存临时表」,表里面有三个字段:goods_id,goods_name 和 num;

2.当第一次这个goods_id=1对应 goods_name=面包 时,那么这个 id 对应 goods_name 就是面包,就算后面这个 id 对应的是火腿面包,鸡腿面包,这都不管,只要第一个是面包,那就固定是这个名称了。这叫先到先得原则。

如果你的 8.0 版本不想要 ONLY_FULL_GROUP_BY 模式,那关闭就可以了。

四、group by 如何优化

group by 在使用不当的时候,很容易就会产生慢 SQL 问题。因为它既用到临时表,又默认用到排序。有时候还可能用到磁盘临时表。

这里总结 4 点优化经验

  • 分组字段加索引

  • order by null 不排序

  • 尽量使用内存临时表

  • SQL_BIG_RESULT

4.1、分组字段加索引

-- 我们给goods_id添加索引alter table order_info add index idx_goods_id (goods_id)

复制代码

然后再看下执行计划

很明显 之前的 Using temporary 和 Using filesort 都没有了,只有 Using index(使用索引了)

4.2、order by null 不排序

如果需求是不用排序,我们就可以这样做。在 sql 末尾加上 order by null

select goods_id, count(*) as numfrom order_infogroup by goods_idorder by null

复制代码

但是如果是已经走了索引,或者说 8.0 的版本,那都不需要加 order by null,因为上面也说了 8.0 默认就是不排序的了。

4.3、尽量使用内存临时表

因为上面也说了,临时表也分为内存临时表和磁盘临时表。如果数据量实在过大,大到内存临时表都不够用了,这时就转向使用磁盘临时表。

内存临时表的大小是有限制的,mysql 中 tmp_table_size 代表的就是内存临时表的大小,默认是 16M。当然你可以自定义社会中适当大一点,这就要根据实际情况来定了。

4.4、SQL_BIG_RESULT

如果数据量实在过大,大到内存临时表都不够用了,这时就转向使用磁盘临时表。

而发现不够用再转向这个过程也是很耗时的,那我们有没有一种方法,可以告诉 mysql 从一开始就使用 磁盘临时表呢?

因此,如果预估数据量比较大,我们使用 SQL_BIG_RESULT 这个提示直接用磁盘临时表。

explain select sql_big_result goods_id, count(*) as numfrom order_infogroup by goods_id

复制代码

从执行结果来看 确实已经不存在临时表了。

五、一个很有意思的优化案例

为了让效果看去明显点,我在这里在数据库中添加了 100 万条数据(整整插了一下午呢)。

同时说明下当前数据库版本是8.0.22

执行得 sql 如下:

select goods_id, count(*) as numfrom order_infowhere pay_time >= '2022-12-01 00:00:00' and pay_time <= '2022-12-31 23:59:59'group by goods_id;

复制代码

5.1、不加任何索引

执行时间是: 0.67秒

我们在执行下 explain

我们发现没有走任何索引,而且有临时表存在,那我是不是考虑给 goods_id 加一个索引?

5.2、仅分组字段加索引

alter table order_info add index idx_goods_id(goods_id);

复制代码

我们在执行下 explain

确实是走了上面创建的idx_goods_id,索引,那查询效率是不是要起飞了?

我们在执行下上面的查询 sql

执行时间是: 21.82秒!

天啦,明明我的分组字段加了索引,而且从执行计划来看确实走了索引,而且也不存在Using temporary临时表了,怎么速度反而下来了,这是为什么呢?

原因:

虽然说我们用到了idx_goods_id 索引,那我们看上图执行计划中  rows = 997982,说明啥,说明虽然走了索引,但是从扫描数据来看依然是全表扫描呢,为什么会这样?

首先 group by 用到索引,那就在索引树上索引数据,但是因为加了 where 条件,还是需要在去表里检索几乎所有的数据, 这样子,还不如直接去表里进行全表扫,这样还更快些。

所以没有索引反而更快了

5.3、查询字段和分组字段建立组合索引

那我们给 pay_time 和 goods_id 建立组合索引呢?

 -- 先删除idx_goods_id索引drop index idx_goods_id on order_info; -- 再新建组合索引alter table order_info add index idx_payTime_goodsId(pay_time,goods_id);

我们在执行下 explain

这次可以很明显的看到

  • Extra 这个字段的Using index 表示该查询条件确实用到了索引,而且是索引覆盖

  • Extra 这个字段的 Using temporary 表示在执行分组的时候使用了临时表

为什么加了索引还会有临时表存在呢,其实原因很简单

range 类型查询字段后面的索引全都无效

因为 pay_time 是范围查询,索引 goods_id 无效,所以分组一样有临时表存在!

我们在看下查询时间

执行时间是: 0.04秒!

是不是快到起飞,虽然我们从执行计划来看依然还是存在 Using temporary ,但查询速度却非常快。

关键点就在Using index(索引覆盖),虽然排序是无法走索引了,但是不需要回表查询,这个效率提升是惊人的!

5.4、仅查询字段建立索引

上面说了就算建立了 pay_time,goods_id 组合索引,对于 goods_id 分组依然不走索引的。

这里我自建立 pay_time 单个索引

 -- 先删除组合索引drop index idx_payTime_goodsId on order_info; -- 再新建单个索引alter table order_info add index idx_pay_time(pay_time);

这次可以很明显的看到

  • Extra 这个字段的using index condition 需要回表查询数据,但是有部分数据是在二级索引过滤后,再回表查询数据,减少了回表查询的数据行数

  • Extra 这个字段的Using MRR 优化器将随机 IO 转化为顺序 IO 以降低查询过程中 IO 开销

  • Extra 这个字段的 Using temporary 表示在执行分组的时候使用了「临时表」

查看查询时间

执行时间 0.56秒!

从结果看出,跟最开始不加索引查询速度相差不多,原因是什么呢?

最主要原因就是虽然走了索引,但是依然还需要回表查询,查询效率并没有提高多少!

那我们思考如何优化呢,既然上面走了回表,我们是不是可以不走回表查询,这里修改下 sql

select goods_id, count(*) as numfrom order_infowhere id in ( select id from order_info where pay_time >= '2022-12-01 00:00:00' and pay_time <= '2022-12-31 23:59:59')group by goods_id;

查看查询时间

执行时间 0.39秒!

速度确实有提升,我们在执行下 explain

我们可以看到 没有了using index condition,而有了Using index,说明不需要再回表查询,而是走了索引覆盖!

本篇到这里就结束啦,希望整篇文章对你有帮助哦

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/42584.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kamiya丨Kamiya艾美捷狗CRP ELISA说明书

Kamiya艾美捷狗CRP ELISA预期用途&#xff1a; 狗CRP ELISA是一种高灵敏度的双位点酶联免疫分析&#xff08;ELISA&#xff09;&#xff0c;用于定量测定狗生物样品中的C反应蛋白&#xff08;CRP&#xff09;。仅供研究使用。 引言 急性期蛋白质是血浆蛋白质&#xff0c;其在…

开发者实践|如何实现云开发场景联动(内附结构图和教学视频)

一千个住户有一千种生活习惯&#xff0c;智能家居如何才能根据用户个性化的需求&#xff0c;实现真正的“智能”&#xff1f;这就需要家居产品之间智能排列&#xff0c;组合成多样化的场景联动模式。 下面我们就来说说如何通过Tuya OpenAPI来实现云开发场景联动&#xff0c;满…

使用 qrcode 生成二维码

qrcode 1 安装2 引入3 使用3.1 方法1 &#xff1a;QRCode.toCanvas()3.2 方法2 &#xff1a;QRCode.toDataURL()4 完整示例qrcode 是一个用于生成二维码的 JavaScript 库。主要是通过获取 DOM 的标签,再通过 HTML5 Canvas 绘制而成 1 安装 npm install --save qrcode2 引入 …

本地部署开发环境过程和遇到的问题总结

一、 软件安装&#xff0c;环境配置 安装配置jdk(不能只有jre)安装配置maven安装配置git安装idea 二、拉项目 在云效代码管理中选择对应的库&#xff0c;复制其仓库地址 在idea中配置git 参考https://blog.csdn.net/qq_62701769/article/details/125029536 拉代码 URL输入…

叠氮试剂知识整理:6-azidohexan-1-amine|349553-73-7|6-叠氮基己胺

产品描述&#xff1a; 6-azidohexan-1-amine呈淡黄色或无色油状&#xff0c;含有叠氮基。叠氮化物可以与炔烃、DBCO和BCN进行铜催化的点击化学反应。NHS酯可以与胺基反应&#xff0c;形成稳定的酰胺键。点击化学反应效率高&#xff0c;即使在低浓度(μM)和低温(4oc)下&#xff…

安卓蓝牙耳机哪个牌子好?安卓手机蓝牙耳机推荐

目前&#xff0c;蓝牙耳机迅速流行于人们的视野当中&#xff0c;在各种场合中&#xff0c;肯定少不了一款蓝牙耳机&#xff0c;但现在的运动耳机可谓五花八门&#xff0c;虽然苹果手机广泛于市场&#xff0c;但是安卓品牌也在不断的采用最新技术的研发&#xff0c;下面是小编整…

Baklib知识分享|企业产品需求文档的特点

产品或服务是公司的核心。没有产品就没有业务。 虽然我们一直在努力追求卓越的产品&#xff0c;但我们有可能不能够达成目标。你可能经常基于你对产品的基本想法来构建产品。有时&#xff0c;您可能只是运行迭代或向您的产品团队提出特性需求。你不能只是临时拼凑一些构成你业…

分组后统计查询

【问题】 I am trying to select top values in a column based on the variable/field in another column. it is a very large tab delimited file. Input: Names col2 col3 col4 A A1 def 10 A A1 BBB 10 A A1 CED 10 A A1 fff 7.5 B B1…

2022秋季信息安全技术(期末复习)

目录 问答题考点&#xff1a; 1.简单替换密码加解密 2.DES算法的原理&#xff1a; ​编辑3.RSA算法的全过程&#xff1a; 4.基于公开密钥的数字签名方案 5.单项散列函数的特点&#xff1a; 6.简述信息的完整性、可用性、保密性 7.简述数字水印的主要特征 8.论述公开密钥…

Qt 工程添加windows库文件

文章目录背景编译出错查看openTrace函数在Qt 工程中添加库文件添加后pro文件背景 项目中用到了“C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\shared\evntrace.h" 文件中的接口OpenTrace 等。 编译出错 error LNK2019&#xff1a; 查看openTrace函数…

R语言中的LDA模型:对文本数据进行主题模型topic modeling分析

主题建模 在文本挖掘中&#xff0c;我们经常收集一些文档集合&#xff0c;例如博客文章或新闻文章&#xff0c;我们希望将其分成组&#xff0c;以便我们可以分别理解它们。最近我们被客户要求撰写关于主题模型的研究报告&#xff0c;包括一些图形和统计输出。主题建模是对这些…

SoftLabel

来从标签平滑和知识蒸馏理解&#xff0c;先探讨一下hard label和soft label之间的关系&#xff0c;然后介绍一下如何用可靠的方法得到蕴含更多信息的soft label&#xff0c;其中主要包含标签平滑和知识蒸馏两种经典方法。 深度学习领域中&#xff0c;通常将数据标注为hard lab…

Listener(监听器)-ServletContextListener

开发工具与关键技术&#xff1a; IDEA 撰写时间&#xff1a;2022/11/28 监听器可以监听就是在 application &#xff0c; session &#xff0c; request 三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件。 ​ application 是 ServletContext 类型的…

linux-划分分区挂载磁盘

本操作以该场景为例&#xff0c; 当云主机挂载了—块新的数据盘时&#xff0c; 使用fdisk分区工具将该数据盘设为主分区&#xff0c; 分区方式默认设置为MBR, 文件系统设为ext4格式&#xff0c; 挂载在 “/mnt/sdc"下&#xff0c; 并设置开机启动自动挂载。 1.执行以…

Vue3常用知识点总结

一、vue3的基本介绍及项目创建 Vue是一套用于构建用户界面的渐进式框架。Vue.js 3.0 "One Piece" 正式版在2020年9月份发布,经过了2年多开发, 100位贡献者, 2600次提交, 600次PR&#xff0c;同时Vue3也支持Vue2的大多数特性,且,更好的支持了TypeScript,也增加了很多的…

Linux 进程信号深剖

目录传统艺能&#x1f60e;概念&#x1f914;信号发送&#x1f914;信号记录&#x1f914;信号产生&#x1f914;常见信号处理方式&#x1f914;终端按键产生信号&#x1f914;核心转储&#x1f60b;如何调试&#x1f914;系统函数发送信号&#x1f914;raise函数&#x1f914;…

单片机驱动LCD

单片机驱动LCD的方法有很多&#xff0c;网络上也有很多配套的例程&#xff0c;但是&#xff0c;网上例程千千万&#xff0c;谁是你的“no.1”。 今天给大家分享一个以面向对象的方式用单片机驱动LCD的思路。 LCD种类概述 在讨论怎么写LCD驱动之前&#xff0c;我们先大概了解…

无线传感器网络:差错控制

文章目录Power controlAutomatic repeat request (ARQ)Stop and WaitGo Back NSelective RepeatForward error correction (FEC)Hybrid ARQ (HARQ)References严格意义来说&#xff0c;文章中提到的数据包都应为数据帧更为恰当&#xff0c;这里提出&#xff0c;希望不要给大家造…

3.9 拆解小红书爆文,流量密码原来是这些【玩赚小红书】

具体如何撰写1篇爆文&#xff0c;解决这一问题前&#xff0c;我们先把爆文四要素&#xff0c;选题、首图、标题和正文进行拆解&#xff0c;真正做到选题正确&#xff0c;用户坐着封面、标题、正文滑滑梯&#xff0c;站内留下点赞评&#xff0c;站外去拔草。 ​ 一、选题&#…

了解模型开发与部署,看这里!

11月24日下午15&#xff1a;00顶象第十期业务安全系列大讲堂系列课程《Xintell 模型平台 》正式开讲。 顶象人工智能专家&研发总监无常从模型平台的现状与需求出发&#xff0c;带大家了解了模型平台的开发环境与部署环境&#xff0c;并且就顶象的Xintell 模型平台 为大家做…