mysql中 distinct 和 group by 哪个效率更高?
省流
首先,给出一个大致的结论(完整解释请继续阅读):
- 有索引时,
GROUP BY和DISTINCT的效率几乎相同,都可以利用索引优化。 - 没有索引时,
DISTINCT的效率通常更高。原因在于GROUP BY会进行排序,可能会触发filesort,导致 SQL 执行变慢。
那么,接下来的问题是:
- 为什么在有索引的情况下,
GROUP BY和DISTINCT会有相同的效率? - 为什么
GROUP BY会进行排序操作,如何影响性能?
这些问题将通过本文逐一解析。首先,让我们快速了解一下 DISTINCT 和 GROUP BY 的基本使用。
DISTINCT 的使用
DISTINCT 的基本用法
SELECT DISTINCT columns FROM table_name WHERE where_conditions;例如,假设有一个学生表:
mysql> select distinct age from student;
+------+
| age |
+------+
| 10 |
| 12 |
| 11 |
| NULL |
+------+
4 rows in set (0.01 sec)DISTINCT 用来筛选唯一不同的值。如果某个字段包含 NULL,它会将所有 NULL 视为相同,保留一个 NULL,而其他的 NULL 会被去掉。
多列去重
SELECT DISTINCT column1, column2 FROM table_name;例如:
mysql> select distinct sex, age from student;
+--------+------+
| sex | age |
+--------+------+
| male | 10 |
| female | 12 |
| male | 11 |
| male | NULL |
| female | 11 |
+--------+------+
5 rows in set (0.01 sec)在多列去重时,只有所有列的组合相同,才会被视为重复。
GROUP BY 的使用
GROUP BY 的基本用法
GROUP BY 的语法与 DISTINCT 类似,用来对结果进行分组:
SELECT columns FROM table_name GROUP BY columns;例如:
mysql> select age from student group by age;
+------+
| age |
+------+
| 10 |
| 12 |
| 11 |
| NULL |
+------+
4 rows in set (0.01 sec)多列去重
SELECT columns FROM table_name GROUP BY columns;例如:
mysql> select sex, age from student group by sex, age;
+--------+------+
| sex | age |
+--------+------+
| male | 10 |
| female | 12 |
| male | 11 |
| male | NULL |
| female | 11 |
+--------+------+
5 rows in set (0.01 sec)语法差异
GROUP BY 可以更灵活地进行分组,不仅仅是单列,还可以组合多列,分组后可以对每一组进行复杂的计算、聚合等处理。而 DISTINCT 只是做简单的去重。
例如:
mysql> select sex, age from student group by sex;
+--------+-----+
| sex | age |
+--------+-----+
| male | 10 |
| female | 12 |
+--------+-----+
2 rows in set (0.01 sec)DISTINCT 和 GROUP BY 的原理
在大多数情况下,DISTINCT 和 GROUP BY 本质上做的是同一件事:分组操作。这两者都可以使用索引扫描来优化查询性能。例如,以下两条 SQL 语句执行的计划几乎相同:
mysql> explain select int1_index from test_distinct_groupby group by int1_index;
mysql> explain select distinct int1_index from test_distinct_groupby;在 EXPLAIN 结果中的 Extra 列,我们可以看到两者都使用了 Using index for group-by,表示它们都在利用索引进行分组操作。
GROUP BY 的隐式排序
不过,需要注意的是,MySQL 8.0 之前的版本,GROUP BY 会进行隐式排序,这就意味着它会对分组的列进行排序,甚至可能会触发 filesort 操作,这通常会影响性能,尤其是在没有合适索引时。比如以下的 SQL:
mysql> explain select int6_bigger_random from test_distinct_groupby GROUP BY int6_bigger_random;
+----+-------------+-----------------------+------------+------+---------------+------+---------+------+-------+----------+---------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------------------+------------+------+---------------+------+---------+------+-------+----------+---------------------------------+
| 1 | SIMPLE | test_distinct_groupby | NULL | ALL | NULL | NULL | NULL | NULL | 97402 | 100.00 | Using temporary; Using filesort |
+----+-------------+-----------------------+------------+------+---------------+------+---------+------+-------+----------+---------------------------------+上面我们看到 Extra 中出现了 Using temporary; Using filesort,意味着 MySQL 在执行 GROUP BY 时,创建了临时表并进行了排序,导致性能下降。
MySQL 8.0 的优化
从 MySQL 8.0 开始,GROUP BY 不再默认进行隐式排序,这减少了不必要的性能消耗。如果没有索引可用来排序,MySQL 会尽量避免使用 filesort,并尝试优化查询性能。因此,在 MySQL 8.0 及之后版本,GROUP BY 和 DISTINCT 的效率更加相似。
总结
- 有索引时,
DISTINCT和GROUP BY都能有效地利用索引,效率差不多。 - 没有索引时,
DISTINCT通常会比GROUP BY更高效,因为GROUP BY可能会进行隐式排序,导致触发filesort,增加了额外的计算开销。 - 从 MySQL 8.0 开始,
GROUP BY不再进行隐式排序,因此在没有索引的情况下,GROUP BY和DISTINCT的性能也变得相似。
推荐使用 GROUP BY
GROUP BY语义更为清晰,特别是进行复杂数据处理时(例如使用HAVING过滤数据)。GROUP BY能处理更复杂的业务逻辑,比如聚合操作,数据筛选等,而DISTINCT只是简单的去重。
因此,如果你需要做更复杂的查询处理,推荐使用 GROUP BY,而如果只是简单去重,DISTINCT 是更简洁的选择。