MySQL中的 distinct 和 group by 去重效率区别

Posted 菜鸟小杰子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL中的 distinct 和 group by 去重效率区别相关的知识,希望对你有一定的参考价值。

一、distinct

distinct的作用

mysql中,distinct关键字的主要作用就是对数据库表中一个或者多个字段重复的数据进行过滤,只返回其中的一条数据给用户,distinct只可以在select中使用

distinct的原理

distinct进行去重的主要原理是通过先对要进行去重的数据进行分组操作,然后从分组后的每组数据中去一条返回给客户端,在这个分组的过程可能会出现两种不同的情况:

distinct 依赖的字段全部包含索引:

该情况mysql直接通过操作索引对满足条件的数据进行分组,然后从分组后的每组数据中去一条数据。

distinct 依赖的字段未全部包含索引:

该情况由于索引不能满足整个去重分组的过程,所以需要用到临时表,mysql首先需要将满足条件的数据放到临时表中,然后在临时表中对该部分数据进行分组,然后从临时表中每个分组的数据中去一条数据,在临时表中进行分组的过程中不会对数据进行排序。

distinct的语法:

select distinct expression[,expression…] from tables [where conditions];

在使用distinct的过程中主要注意一下几点:

在对字段进行去重的时候,要保证distinct在所有字段的最前面
如果distinct关键字后面有多个字段时,则会对多个字段进行组合去重,只有多个字段组合起来的值是相等的才会被去重

二、group by

groupby在Mysql8.0之前会进行隐式排序,导致触发filesort,sql执行效率低下,Mysql8.0开始,Mysql就删除了隐式排序

隐式排序

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

MySQL :: MySQL 5.7 Reference Manual :: 8.2.1.14 ORDER BY Optimization

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 指示符的情况下也会进行排序)。然而,GROUPBY进行显式或隐式排序已经过时(deprecated)了,要生成给定的排序顺序,请提供 ORDER BY 子句。

所以,在Mysql8.0之前,Group by会默认根据作用字段(Groupby的后接字段)对结果进行排序。在能利用索引的情况下,Group by不需要额外进行排序操作;但当无法利用索引排序时,Mysql优化器就不得不选择通过使用临时表然后再排序的方式来实现GROUPBY了。且当结果集的大小超出系统设置临时表大小时,Mysql会将临时表数据copy到磁盘上面再进行操作,语句的执行效率会变得极低。这也是Mysql选择将此操作(隐式排序)弃用的原因。

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

MySQL :: MySQL 8.0 Reference Manual :: 8.2.1.16 ORDER BY Optimization

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 claus

大致解释一下:

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

三、distinct 和 group by 比较

在语义相同,有索引的情况下:

group by和distinct都能使用索引,效率相同。因为groupby和distinct近乎等价,distinct可以被看做是特殊的group by。

在语义相同,无索引的情况下:

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

推荐group by的原因:

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

distinct 主要是对数据两两进行比较,需要遍历整个表

group by 是在查询时先把数据按照分组字段分组出来再查询,当数据量较大时,group by 速度要优于 distinct

Distinct,REGEXP适用于MYSQL中的Field和CONCAT_GROUP来去除存储过程中的重复词

【中文标题】Distinct,REGEXP适用于MYSQL中的Field和CONCAT_GROUP来去除存储过程中的重复词【英文标题】:Distinct,REGEXP apply to Field and CONCAT_GROUP in MYSQL to remove repeated words to stored procedure 【发布时间】:2013-08-26 20:31:39 【问题描述】:

上下文:

我有下表(示例):

| ID  |   name   | COUNTRY                  |
---------------------------------------------
| 1   | cristian | FRANCIA,HOLANDA,ALEMANIA |
| 2   | Andrea   | FRANCIA,ESPAÑA,BELGICA   |
| 3   | F***   | BELGICA,ALEMANIA         |

我需要将所有国家/地区放在一个字段中,但我需要没有重复值。

所以,我正在尝试以下查询:

select GROUP_CONCAT(DISTINCT(COUNTRY)) FROM  Usuario;

或使用正则表达式 Some like :

select GROUP_CONCAT(DISTINCT(COUNTRY)) FROM  Usuario
WHERE GROUP_CONCAT(COUNTRY) REGEXP 'somepattern'

错误的答案是下一个:

FRANCIA,HOLANDA,ALEMANIA,FRANCIA,ESPAÑA,BELGICA,BELGICA,ALEMANIA

预期的答案是:

FRANCIA,HOLANDA,ALEMANIA,ESPAÑA,BELGICA

或者制作一些存储过程

如何得到预期的答案,对N个值不同的值

感谢您的知识和时间!

【问题讨论】:

你的表结构不好 对不起,我现在这是最好的方法,但我无法控制数据库。 【参考方案1】:

在 MySQL 中没有内置函数可以做到这一点。

可以在 MySQL 中进行大量字符串处理,但它很难看,并且必须知道逗号分隔列表中字符串值的数量是有限的。

这是从所有逗号分隔列表中获取不同字段值列表的一种方法:

SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(country,','),',',1),',',-1) AS fld
  FROM mytable
HAVING fld <> ''
 UNION
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(country,','),',',2),',',-1) AS fld
  FROM mytable
HAVING fld <> ''
 UNION
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(country,','),',',3),',',-1) AS fld
  FROM mytable
HAVING fld <> ''
 UNION
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(country,','),',',4),',',-1) AS fld
  FROM mytable
HAVING fld <> ''
 UNION
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(country,','),',',5),',',-1) AS fld
  FROM mytable
HAVING fld <> ''
 UNION
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(country,','),',',6),',',-1) AS fld
  FROM mytable
HAVING fld <> ''
 ORDER BY 1

我将把它作为一个练习,让你弄清楚它在做什么以及它是如何工作的。

现在每个值都在单独的行中,我们认为您可能希望保持这种方式。

很容易将该查询包装在另一个查询中,并使用 GROUP_CONCAT 函数,并返回包含逗号分隔列表的字符串值的单行。

【讨论】:

有什么方法可以在 mysql 中创建一个 n 元素的过程吗?【参考方案2】:

mysql 无法像这种方式获得独特性。

首先,以这种方式存储值很糟糕。

你有两个解决方案:

1- 规范化您的数据库。

2-从表中获取值并使用 php explode() ,并使用 array_unique 删除重复值。

【讨论】:

【参考方案3】:

最初,您必须创建新的查找表,其中每行包含国家/地区名称(此处为 COUNTRY_TABLE)。 那就试试这个吧。

SELECT (SELECT GROUP_CONCAT(DISTINCT c.COUNTRYNAME) FROM  COUNTRY_TABLE AS c WHERE  FIND_IN_SET(c.COUNTRYNAME,COUNTRY) ) AS UNIQUECOUNTRYNAMES FROM Usuario;

【讨论】:

以上是关于MySQL中的 distinct 和 group by 去重效率区别的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 中的 distinct 和 group by 哪个效率更高?

京东一面:MySQL 中的 distinct 和 group by 哪个效率更高?问倒一大遍。。

MySQL:GROUP_CONCAT 中的 DISTINCT 删除相同的值(不重复)

京东一面:MySQL 中的 distinct 和 group by 哪个效率更高?问倒一大遍。。

京东一面:MySQL 中的 distinct 和 group by 哪个效率更高?问倒一大遍。。

京东一面:MySQL 中的 distinct 和 group by 哪个效率更高?太刁钻了吧!