MySQL 中的 distinct 和 group by 的性能比较

news2025/7/20 17:25:28

1 先说大致的结论(完整结论在文末)

  • 在语义相同,有索引的情况下:group by和 distinct 都能使用索引,效率相同。

  • 在语义相同,无索引的情况下:distinct 效率高于group by。原因是 distinct 和 group by都会进行分组操作,但group by可能会进行排序,触发 filesort,导致 sql 执行效率低下。

基于这个结论,你可能会问:

  • 为什么在语义相同,有索引的情况下,group by和 distinct 效率相同?

  • 在什么情况下,group by会进行排序操作?

带着这两个问题找答案。接下来,我们先来看一下 distinct 和group by的基础使用。

2 distinct 的使用

2.1 distinct 用法

SELECT DISTINCT columns FROM table_name WHERE where_conditions;

例如:

select DISTINCT age from employee;

输出:

DISTINCT 关键词用于返回唯一不同的值。放在查询语句中的第一个字段前使用,且作用于主句所有列。

如果列具有 NULL 值,并且对该列使用DISTINCT子句,MySQL 将保留一个 NULL 值,并删除其它的 NULL 值,因为DISTINCT子句将所有 NULL 值视为相同的值。

2.2 distinct 多列去重

distinct 多列的去重,则是根据指定的去重的列信息来进行,即只有所有指定的列信息都相同,才会被认为是重复的信息。

SELECT DISTINCT columns FROM table_name WHERE where_conditions;

例如:

select DISTINCT age, manager from employee;

输出:

3 group by 的 使用

对于基础去重来说,group by 的使用和 distinct 类似。

3.1 单列去重

语法:

SELECT columns FROM table_name WHERE where_conditions GROUP BY columns;

例如:

SELECT age FROM employee GROUP BY age;

输出:

3.2 多列去重

语法:

SELECT columns FROM table_name WHERE where_conditions GROUP BY columns;

例子:

SELECT age, manager FROM employee GROUP BY age, manager;

输出:

3.3 区别示例

两者的语法区别在于,group by可以进行单列去重,group by的原理是先对结果进行分组排序,然后返回每组中的第一条数据。且是根据group by的后接字段进行去重的。

例如:

SELECT age, manager FROM employee GROUP BY age;

输出:

4 distinct 和 group by 原理

在大多数例子中,DISTINCT可以被看作是特殊的GROUP BY,它们的实现都基于分组操作,且都可以通过松散索引扫描、紧凑索引扫描(关于索引扫描的内容会在其他文章中详细介绍,就不在此细致介绍了)来实现。

DISTINCT和GROUP BY都是可以使用索引进行扫描搜索的。例如以下两条 sql(只单单看表格最后 extra 的内容),我们对这两条 sql 进行分析,可以看到,在 extra 中,这两条 sql 都使用了紧凑索引扫描Using index for group-by。

所以,在一般情况下,对于相同语义的DISTINCT和GROUP BY语句,我们可以对其使用相同的索引优化手段来进行优化。

但对于GROUP BY来说,在 MYSQL8.0 之前,GROUP Y默认会依据字段进行隐式排序。

可以看到,下面这条 sql 语句在使用了临时表的同时,还进行了 filesort。

5 隐式排序

5.1 对于隐式排序,我们可以参考 MySQL 官方的解释:

https://dev.mysql.com/doc/refman/5.7/en/order-by-optimization.html

GROUP BY implicitly sorts by default (that is, in the absence of ASC or DESC designators for GROUP BY columns). However, relying on implicit GROUP BY sorting (that is, sorting in the absence of ASC or DESC designators) or explicit sorting for GROUP BY (that is, by using explicit ASC or DESC designators for GROUP BY columns) is deprecated. To produce a given sort order, provide an ORDER BY clause.

大致解释一下:

GROUP BY 默认隐式排序(指在 GROUP BY 列没有 ASC 或 DESC 指示符的情况下也会进行排序)。然而,GROUP BY 进行显式或隐式排序已经过时(deprecated)了,要生成给定的排序顺序,请提供 ORDER BY 子句。

所以,在 MySQL8.0 之前,GROUP BY会默认根据作用字段(GROUP BY的后接字段)对结果进行排序。在能利用索引的情况下,GROUP BY不需要额外进行排序操作;但当无法利用索引排序时,MySQL 优化器就不得不选择通过使用临时表然后再排序的方式来实现GROUP BY了。

且当结果集的大小超出系统设置临时表大小时,MySQL 会将临时表数据 copy 到磁盘上面再进行操作,语句的执行效率会变得极低。这也是 MySQL 选择将此操作(隐式排序)弃用的原因。

5.2 基于上述原因,Mysql 在 8.0 时,对此进行了优化更新:

https://dev.mysql.com/doc/refman/8.0/en/order-by-optimization.html

Previously (MySQL 5.7 and lower), GROUP BY sorted implicitly under certain conditions. In MySQL 8.0, that no longer occurs, so specifying ORDER BY NULL at the end to suppress implicit sorting (as was done previously) is no longer necessary. However, query results may differ from previous MySQL versions. To produce a given sort order, provide an ORDER BY clause.

大致解释一下:

从前(MySQL5.7 版本之前),Group by 会根据确定的条件进行隐式排序。在 MySQL 8.0 中,已经移除了这个功能,所以不再需要通过添加 order by null 来禁止隐式排序了,但是,查询结果可能与以前的 MySQL 版本不同。要生成给定顺序的结果,请按通过 ORDER BY指定需要进行排序的字段。

6 结论

根据上述描述,我们的结论也就出来了

  • 在语义相同,有索引的情况下: group by和 distinct 都能使用索引,效率相同。因为group by和distinct近乎等价,distinct 可以被看做是特殊的group by。

  • 在语义相同,无索引的情况下: distinct效率高于group by。原因是distinct 和 group by都会进行分组操作,但group by在 MySQL8.0 之前会进行隐式排序,导致触发 filesort,sql 执行效率低下。但从 MySQL8.0 开始,MySQL 就删除了隐式排序,所以,此时在语义相同,无索引的情况下,group by和distinct的执行效率也是近乎等价的。

相比于distinct来说,group by的语义明确。且由于 distinct 关键字会对所有字段生效,在进行复合业务处理时,group by的使用灵活性更高,group by能根据分组情况,对数据进行更为复杂的处理,例如通过having对数据进行过滤,或通过聚合函数对数据进行运算。

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

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

相关文章

【软件开发】基于PyQt5开发的标注软件

这里是基于PyQt5写的面向目标检测的各类标注PC端软件系统。目前现有的labelme软件和labelImg开源软件无法满足特殊数据集的标注要求,而且没有标注顺序的报错提示。当然我设计的软件就会不具有适用性了(毕竟从下面开发的软件可以明显看出来我做的基本上是…

spark-submit报错

spark-submit --class ${main_class} \ --master yarn \ --deploy-mode client \ --driver-memory 8g \ --executor-memory 6g \ --num-executors 1 \ --executor-cores 34 \ ${HOME_PATH}/obs_finance-1.0-SNAPSHOT-jar-with-dependencies.jar ${year_month} 注意这个34超过…

【教学典型案例】28.单表的11个Update接口--MyBatis

目录一:背景介绍二:前期准备引入pom依赖MyBatis配置文件数据库连接文件MyBatis配置类三:代码编写Mapper编写接口通用mapper实体pojojunit测试编写测试结果四:总结一:背景介绍 在进行项目开发编写更新接口时&#xff0…

顶级动漫IP加持之下,3A策略游戏Mechaverse如何改变GameFi

2021年是元宇宙发展的元年,元宇宙与GameFi创造了一波又一波市场热点。在经历第一波热潮之后,元宇宙的到来让不少人看到了加密市场的潜力,同时大家也意识到这将是未来的重要方向。如何将元宇宙推向更广阔的市场,让更多人能够轻松进…

项目管理工具DHTMLX Gantt灯箱元素配置教程:显示任务内容

DHTMLX Gantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表。可满足项目管理应用程序的大部分开发需求,具备完善的甘特图图表库,功能强大,价格便宜,提供丰富而灵活的JavaScript API接口,与各种服务器端技术&am…

神经网络(容易被忽视的基础知识)

主要内容: 基本神经元作为线性分类器的单个神经元为什么要是深度神经网而不是”肥胖“神经网络?为什么在人工神经网络中的神经元需要激活函数?一些主要的激活函数神经网络中的偏置有什么意义?初始化神经网络的参数可以全为0吗&am…

【java】java基本类型和包装类型的区别

文章目录简介1.包装类型可以为 null,而基本类型不可以2.包装类型可用于泛型,而基本类型不可以3.基本类型比包装类型更高效4.自动装箱和自动拆箱简介 Java 的每个基本类型都对应了一个包装类型,比如说 int 的包装类型为 Integer,d…

ECOLOY直接更换流程表单后导致历史流程中数据为空白的解决方案

用户反馈流历史流程打开是空白了没有内容。 一、问题调查分析: 工作流“XX0204 员工培训协议审批流程”workflowId37166产生的7个具体流程中,创建日期为2021年的4个具体流程原先引用的数据库表单应该是“劳动合同签订审批表”(formtable_main_190)&…

JavaScript-缓存

参考资料彻底解决让用户清一下浏览器缓存浏览器缓存彻底理解浏览器的缓存机制彻底弄懂前端缓存浅解强缓存和协商缓存浏览器缓存策略(强缓存和协商缓存)一文搞定Http缓存-强制缓存与协商缓存前端浏览器缓存知识梳理ASP.NET Core 中使用缓存IIS中设置Cache-Control是什么当我们第…

[Gin]框架底层实现理解(一)

前言:路由原理———压缩字典 这边简单讲一下gin非常重要的一个基点,也就是他作为go web框架的一个亮点 也就是Trie树和压缩字典算法 gin 通过树来存储路由,讲路由的字符拆解为一个个的结点,在获取handler函数时,会…

项目管理系统软件有哪些?这10款最好用的项目管理软件值得推荐

项目管理系统软件有哪些?这10款好用的项目管理系统软件值得推荐 如今企业规模不断扩大,业务逐渐复杂化,项目管理系统已经成为现代企业管理中不可或缺的一环; 而项目管理系统软件不仅可以保证项目按时完成,提高团队的…

【Java基础 下】 029 -- 多线程

目录 一、为什么要有多线程? 1、线程与进程 2、多线程的应用场景 3、小结 二、多线程中的两个概念(并发和并行) 1、并发 2、并行 3、小结 三、多线程的三种实现方式 1、继承Thread类的方式进行实现 2、实现Runnable接口的方式进行实现 3、利用…

MySQL简述

一、什么是数据库 数据库(DB,DataBase) 概念:数据仓库,相当于一款软件,安装在操作系统(Windows,Linux,mac,…)之上,可以存储大量的数…

nvm安装及使用(win)

一、安装nvm 下载地址:下载之后安装包安装 安装路径不要有中文或者空格 安装时尽量不要选择系统盘(安装在系统盘可能会涉及到权限问题) 安装前一定要把 node 的环境变量和 npm 的环境变量删除,否则在切换 node 版本时会报错 二、…

SCL_PFENET跑通填坑

1.数据准备:VOC2012数据集,initmodel文件夹(预训练模型),SegmentationClassAug数据2.训练部分:训练部分没什么需要改动的,也就改一下选择的配置文件。在config文件夹里有关于coco和voc数据的配置…

备战蓝桥杯Day3python——迭代器的使用

python封装的迭代器 我们接下来介绍用于返回笛卡尔积的Permutations函数 Permutations(iterator, r) r:表示进行迭代后返回的长度 实例: >>> from itertools import permutations >>> a abc >>> for i in permutations(a,3):print …

Java深拷贝,浅拷贝

一、浅拷贝: (1) 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性复制一份给新的对象。因为是两份不同的数据,所以对其中一的对象的成员变量值进行修改,不会影响另…

大考在即 百度版ChatGPT会翻车吗?

文心一言的发布会定档3月16日,不出意外,百度创始人李彦宏、CTO王海峰将出现在北京总部的发布会现场。这是百度版ChatGPT最新的官方消息,2月7日,文心一言首次官宣,当时称,产品“3月见”。 3月如期而至&…

C语言再学习 -- __attribute__详解

一、attribute 介绍 __attribute__是一个编译属性,用于向编译器描述特殊的标识、错误检查或高级优化。它是GNU C特色之一,系统中有许多地方使用到。__attribute__可以设置函数属性(Function Attribute)、变量属性(Var…

JavaScript 如何优雅地获取多层级response中的某个深层次字段 ?. 可选链条(Optional chaining)

文章目录一句话场景:从一个多层级对象中拿一个处在深层次位置的字段MDN 可选链搜索引擎使用一句话 var marriedFlag response.data.userList[0].married; // 如果中间某个对象为空,会报Errorvar marriedFlag response?.data?.userList[0]?.married…