在MySQL的查询逻辑中,GROUP BY子句的核心作用是对数据进行分组聚合,而DISTINCT子句的核心作用是去除查询结果中的重复行。当不需要对分组后的数据进行聚合计算时,GROUP BY子句可以表现出和DISTINCT子句完全一致的效果。

基础概念回顾
DISTINCT子句的作用
DISTINCT用于返回唯一不同的值,它会检查查询结果的所有列,只有当所有列的值都完全相同时,才会判定为重复行并去除。例如有一张用户表user,结构如下:
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
age INT,
city VARCHAR(50)
);
如果我们要查询所有不重复的城市,使用DISTINCT的语句如下:
SELECT DISTINCT city FROM user;
GROUP BY子句的基础作用
GROUP BY会根据指定的列对数据进行分组,相同值的行会被归为同一组,通常配合聚合函数比如COUNT、SUM、AVG等使用,计算每组的统计值。例如统计每个城市的用户数量:
SELECT city, COUNT(*) AS user_count FROM user GROUP BY city;
GROUP BY模拟DISTINCT的实现方式
当GROUP BY子句后面指定的分组列和DISTINCT后面指定的去重列完全一致,并且没有使用任何聚合函数时,GROUP BY会返回每个分组的第一行数据,此时分组后的结果就是去重后的结果,和DISTINCT的效果完全相同。
还是以上面的user表为例,要查询所有不重复的城市,使用GROUP BY的语句如下:
SELECT city FROM user GROUP BY city;
这条语句的执行逻辑是:先按照city列对user表的所有行进行分组,每个不同的city值对应一个分组,然后每个分组只返回一行数据,最终得到的结果就是所有不重复的city值,和DISTINCT的查询结果完全一致。
如果需要同时对多列去重,二者的写法也对应一致:
-- DISTINCT多列去重 SELECT DISTINCT name, city FROM user; -- GROUP BY多列去重 SELECT name, city FROM user GROUP BY name, city;
这两句查询都会返回name和city组合不重复的所有行,效果完全相同。
二者的核心差异
虽然部分场景下GROUP BY可以模拟DISTINCT的效果,但二者本质上有明显区别:
- 设计目的不同:DISTINCT的唯一作用就是去重,而GROUP BY的核心目的是分组后进行聚合计算,去重只是它的附带效果。
- 排序行为不同:在MySQL 5.7之前的版本中,GROUP BY会默认对分组列进行排序,而DISTINCT不会主动排序;MySQL 8.0之后GROUP BY默认不再排序,除非显式使用ORDER BY子句。
- 适用场景不同:如果只需要去重,优先使用DISTINCT,语义更清晰;如果去重后还需要对每组数据进行聚合计算,比如统计每组的数量、求和等,必须使用GROUP BY。
性能对比
在只需要去重的场景下,二者的性能差异通常很小,MySQL的优化器会对两种写法做类似的优化。我们可以通过EXPLAIN语句查看二者的执行计划:
EXPLAIN SELECT DISTINCT city FROM user; EXPLAIN SELECT city FROM user GROUP BY city;
在大多数情况下,两条语句的执行计划中的type、key、Extra等字段都会一致,说明优化器对二者的处理逻辑相同。但如果表上有对应列的索引,DISTINCT的语义更明确,优化器可能会优先选择更高效的索引扫描方式。
使用注意事项
在使用GROUP BY模拟DISTINCT时,需要注意以下两点:
- 不要在GROUP BY查询中随意添加非分组列的字段,比如
SELECT id, city FROM user GROUP BY city,这种写法在严格模式的MySQL中会直接报错,因为id列没有在GROUP BY子句中,也不在聚合函数中,属于非法查询。 - 如果需要对去重后的结果排序,不管是使用DISTINCT还是GROUP BY,都需要显式添加ORDER BY子句,避免依赖默认排序行为导致结果不符合预期。
注意:在SQL标准中,SELECT后面出现的列要么是GROUP BY子句中的列,要么是被聚合函数处理的列,违反这个规则的查询在部分数据库中是无法执行的,MySQL在非严格模式下可能允许这种写法,但不建议这样使用。