当前位置: 首页 > 后端技术 > Java

京东:mysql中distinct和groupby哪个更高效?再问一遍,.

时间:2023-04-01 17:08:26 Java

先说下总的结论(完整的结论在文末):在语义和索引相同的情况下:groupby和distinct都可以使用索引,效率是一样的。在语义相同且没有索引的情况下:distinct比groupby更高效。原因是distinct和groupby都是进行分组操作,但是groupby可能会进行排序,触发filesort,导致SQL执行效率低下。基于这个结论,你可能会问:为什么groupby和distinct在具有相同的语义和索引的情况下具有相同的效率?什么情况下groupby会进行排序操作?找出这两个问题的答案。接下来,我们来看看distinct和groupby的基本使用。推荐一个开源免费的SpringBoot最全教程:https://github.com/javastacks/spring-boot-best-practicedistinctusedistinctusageSELECTDISTINCTcolumnsFROMtable_nameWHEREwhere_conditions;例如:mysql>selectdistinctagefromstudent;+------+|年龄|+------+|10||12||11||NULL|+------+4rowsinset(0.01sec)DISTINCT关键字用于返回唯一不同的值。它用在查询语句的第一个字段之前,作用于主子句的所有列。如果一列有NULL值,而你在该列上使用DISTINCT子句,MySQL将保留一个NULL值并删除其他NULL值,因为DISTINCT子句将所有NULL值视为相同的值。distinct多列去重distinct多列去重是根据指定的去重列信息进行的,即只有指定的所有列信息都相同时,才会认为是重复信息。SELECTDISTINCTcolumn1,column2FROMtable_nameWHEREwhere_conditions;mysql>selectdistinctsex,agefromstudent;+------+------+|性别|年龄|+-------+------+|男性|10||女性|12||男性|11||男性|空||女性|11|+------+-----+5rowsinset(0.02sec)groupby的使用对于基本去重,groupby的使用类似于distinct:singlecolumn去重语法:SELECTcolumnsFROMtable_nameWHEREwhere_conditionsGROUPBYcolumns;执行:mysql>selectagefromstudentgroupbyage;+------+|年龄|+------+|10||12||11||NULL|+------+4rowsinset(0.02sec)多列去重语法:SELECTcolumnsFROMtable_nameWHEREwhere_conditionsGROUPBYcolumns;执行:mysql>selectsex,agefromstudentgroupbysex,age;+------+------+|性别|年龄|+--------+------+|男性|10||女性|12||男性|11||男性|空||女性|11|+-------+-----+5rowsinset(0.03sec)差异示例两者在语法上的区别是groupby可以进行单列去重,原理groupby是先对结果进行分组排序,然后返回每组的第一行数据。并且根据groupby后面的字段去重。例如:mysql>selectsex,agefromstudentgroupbysex;+--------+-----+|性别|年龄|+--------+-----+|男性|10||女性|12|+------+-----+2rowsinset(0.03sec)distinctandgroupby原理大多数情况下,DISTINCT可以看出操作是一种特殊的GROUPBY,它们的实现是基于分组操作,可以通过松散索引扫描和紧缩索引扫描来实现(索引扫描的内容会在其他文章中详细介绍,这里不再介绍)。DISTINCT和GROUPBY都可以使用索引进行扫描搜索。比如下面这两条SQL(只看表末extra的内容),我们分析这两条SQL,可以看到在extra中,这两条SQL使用了compactindexscanUsingindexforgroup-by.因此,一般来说,对于语义相同的DISTINCT和GROUPBY语句,我们可以使用相同的索引优化方法对其进行优化。mysql>explainselectint1_indexfromtest_distinct_groupbygroupbyint1_index;+----+------------+----------------------+------------+--------+----------------+---------+--------+-----+-----+---------+--------------------------+|编号|选择类型|表|分区|类型|可能的键|钥匙|密钥长度|参考|行|过滤|额外|+----+------------+--------------------+----------+--------+----------------+--------+--------+------+------+------------+------------------------+|1|简单|test_distinct_groupby|空|范围|index_1|index_1|5|空|955|100.00|使用索引进行分组|+----+------------+--------------------+------------+--------+------------+--------+--------+--------+------+----------+------------------------+1rowinset(0.05sec)mysql>explainselectdistinctint1_indexfromtest_distinct_groupby;+----+--------------+--------------------+------------+--------+---------------+--------+--------+------+------+----------+------------------------+|编号|选择类型|表|分区|类型|可能的键||密钥长度|参考|行|过滤|额外|+----+------------+---------------------+-------------+--------+----------------+---------+--------+------+--------+------------+-------------------------+|1|简单|test_distinct_groupby|空|范围|index_1|index_1|5|空|955|100.00|使用索引进行分组|+----+----------+--------------------+------------+--------+------------+--------+--------+------+------+---------+--------------------------+1rowinset(0.05sec)但是对于GROUPBY,在MYSQL8.0之前,GROUPY默认是基于字段的隐式排序。可以看到,下面的SQL语句使用了一个临时表,同时也进行了filesort。mysql>explainselectint6_bigger_randomfromtest_distinct_groupbyGROUPBYint6_bigger_random;+----+------------+---------------------+------------+------+----------------+------+----------+--------+--------+---------+-------------------------------+|编号|选择类型|表|分区|类型|可能的键|钥匙|密钥长度|参考|行|过滤|额外|+----+-------------+--------------------+-----------+------+----------------+------+--------+------+--------+------------+------------------------------+1|简单|test_distinct_groupby|空|所有|空|空|空|空|97402|100.00|使用临时的;使用文件排序|+----+------------+----------------------+-------------+------+--------------+------+--------+------+--------+---------+----------------------------------+1rowinset(0.04sec)implicitsortingforimplicit排序,可以参考Mysql官方解释:https://dev.mysql.com/doc/ref...GROUPBY默认情况下隐式排序(即,在没有GROUPBY列的ASC或DESC指示符的情况下)。但是,不推荐依赖隐式GROUPBY排序(即,在没有ASC或DESC指示符的情况下进行排序)或GROUPBY的显式排序(即,通过对GROUPBY列使用显式ASC或DESC指示符)。要生成给定的排序顺序,请提供ORDERBY子句。粗略解释一下:GROUPBY默认隐式排序(指即使GROUPBY列没有ASC或DESC指示符也进行排序)但是,GROUPBY的显式或隐式排序已被弃用。要生成给定的排序顺序,请提供ORDERBY子字符串语句。所以在Mysql8.0之前,Groupby默认会按照角色字段(Groupby之后的字段)对结果进行排序。当可以使用索引时,Groupby不需要额外的排序操作;但是当无法使用索引排序时,Mysql优化器不得不选择使用临时表实现GROUPBY,然后进行排序。而当结果集的大小超过了系统设置的临时表的大小时,Mysql会将临时表的数据复制到磁盘中进行操作,语句的执行效率会变得极低。这就是Mysql选择弃用这个操作(隐式排序)的原因。基于以上原因,Mysql在8.0对此进行了优化更新:https://dev.mysql.com/doc/ref...之前(MySQL5.7及更低版本),GROUPBY在特定条件下隐式排序。在MySQL8.0中,不再发生这种情况,因此不再需要在末尾指定ORDERBYNULL来抑制隐式排序(如之前所做的那样)。但是,查询结果可能与以前的MySQL版本不同。要生成给定的排序顺序,请提供ORDERBY子句。粗略解释一下:以前(Mysql5.7之前的版本),Groupby会隐式按照一定的条件进行排序。在mysql8.0中,该特性已经被移除,因此不再需要通过添加orderbynull来禁用隐式排序,但是查询结果可能与之前的MySQL版本不同。要按给定顺序生成结果,请指定需要按ORDERBY排序的字段。因此,我们的结论也出来了:在相同语义和索引的情况下:groupby和distinct都可以使用索引,效率是一样的。因为groupby和distinct几乎是等价的,distinct可以看作是一种特殊的groupby。在语义相同且没有索引的情况下:distinct比groupby更高效。原因是distinct和groupby都是进行分组操作,但是mysql8.0之前groupby进行的是隐式排序,导致触发filesort,sql执行效率低。但是,从Mysql8.0开始,Mysql删除了隐式排序。因此,在语义相同且没有索引的情况下,groupby和distinct的执行效率几乎是等价的。之所以推荐groupby,是因为groupby的语义更清晰。Groupby可以对数据进行更复杂的处理。与distinct相比,groupby语义清晰。并且由于distinct关键字会作用于所有字段,groupby在进行复合业务处理时更加灵活。groupby可以根据分组情况对数据进行更复杂的处理,比如通过having过滤数据,或者通过聚合函数对数据进行操作。版权声明:本文为CSDN博主“狡猾的肖”原创文章,遵循CC4.0BY-SA版权协议,转载请附上原文出处链接及本声明。