这个问题其实比很多人以为的要复杂得多,特别是面试时面试官带着“刁钻”目的来问的时候。如果你直接回一句“效率差不多”可能还真就挂了。咱们今天就来掰扯清楚:MySQL 里的 DISTINCT
和 GROUP BY
,到底谁更高效,什么场景下各自更合适,以及你该怎么选。
先抛个结论出来——
在有索引并语义等价的情况下,两者几乎效率一样;但在无索引时,DISTINCT
很可能更快,GROUP BY
会拖后腿,主要是那个“filesort”惹的祸。不过光说这些还不够,你得知道为什么。
我自己在项目里就遇到过类似的情况,那时候是为了清洗数据表里的重复记录,用 GROUP BY
总感觉慢得要死,一换成 DISTINCT
,结果就飞了,跑得比我早上打卡还快。
DISTINCT:不动声色地去重
我们先看 DISTINCT
。这玩意儿其实逻辑挺简单:你查出来的所有列,只要整行完全一样,就认为是重复的,只保留一条。常规用法是这样:
SELECT DISTINCT age FROM student;
注意,它对你 SELECT
出来的所有字段生效,不是某个字段,而是组合后的整个“行”维度。
比如你写了:
SELECT DISTINCT sex, age FROM student;
它会认为 (male, 10)
和 (female, 10)
是不一样的,只有相同的性别 + 年龄才算重复。
性能层面来说,DISTINCT
做的是“去重”,但如果你给它一个合适的索引,比如 sex, age
的联合索引,它其实不用全表扫描,而是可以直接走索引。MySQL 执行计划里你会看到一个“Using index for group-by”的提示,虽然字面写的是 group-by,但确实也能用于 DISTINCT
,这有点迷惑初学者。
GROUP BY:看似一样,实则有坑
再看 GROUP BY
。你可能觉得和 DISTINCT
类似,确实在某些简单场景下是一样的,但它本质上是“分组”,然后你可以对每组做聚合。
比如:
SELECT age FROM student GROUP BY age;
这时候看起来和 DISTINCT age
差不多,都是返回不同的年龄值。
但你换成这样:
SELECT sex, age FROM student GROUP BY sex;
MySQL 会返回每个性别的第一条记录的 age,不同的引擎实现可能会选不同的 age,你不能指望它总是按你想的来。所以这个用法在逻辑上和 DISTINCT
就不等价了,结果可能不同。
而更要命的是——在 MySQL 8.0 以前,GROUP BY
是默认会排序的。也就是说哪怕你没写 ORDER BY
,它也偷偷给你搞个排序。如果你选的字段没索引覆盖,MySQL 可能就得生成临时表 + filesort,这个组合就是性能黑洞:
Using temporary; Using filesort
看见这两个词,基本可以判断执行效率不会太好,特别是数据量上来了以后,能慢到你怀疑人生。
MySQL 8.0 的“优化”背锅
MySQL 8.0 改了这个默认行为,不再默认隐式排序。这本来是件好事,但也带来一个副作用:一些之前依赖这个排序特性的代码,升级后结果顺序可能变了。所以如果你用了 GROUP BY
想靠它排序,最好老老实实加上 ORDER BY
。
顺便说句,老版本 MySQL 想避免隐式排序,可以在 GROUP BY
后加 ORDER BY NULL
,这是老派 DBA 常用的骚操作。到了 MySQL 8.0,这步就不需要了。
有索引 vs 无索引:才是关键分水岭
你可能发现了,我们上面讲的这些,性能好坏其实并不是 DISTINCT
和 GROUP BY
本身的锅,而是跟有没有用到索引强相关。
如果你用的是 DISTINCT age
,而 age 刚好有个索引,那这个 SQL 可能直接走索引,连表都不用扫,效率当然高。
但如果你用了 GROUP BY age
,age 没索引,MySQL 就得扫全表、创建临时表、排序,然后再分组,整个流程就重了很多。
也就是说,在“无索引”的前提下:
DISTINCT
去重操作,偏“纯”,只需要判断相同行是否重复GROUP BY
是先分组再聚合,如果没有聚合函数,还会默认排序,开销更大
在有索引时,两者都会用上 index,最终执行计划基本相同,所以几乎没有差异。
我个人怎么选?
老实说,我更倾向用 GROUP BY
。不是因为它快,而是它更灵活。你别看它一开始像是去重,其实真正的杀手锏是——你可以挂聚合函数上去。
比如:
SELECT age, COUNT(*) FROM student GROUP BY age;
一下就能知道每个年龄段有多少人,这是 DISTINCT
做不到的。
而且 GROUP BY
+ HAVING
可以让你对分组之后的结果再过滤,比如:
SELECT age, COUNT(*) as c FROM student GROUP BY age HAVING c > 1;
这比你用 DISTINCT
然后自己去计数、再筛选,简单太多了。
当然,如果你只是为了去重,又没啥其他操作,那 DISTINCT
就挺合适,代码也更直观些,尤其是你不打算扩展逻辑的场景。
写代码别光看“表面效率”
面试官要你比较效率,其实不是让你死记硬背谁更快,而是想看你是否知道“为什么更快”。也就是说,你得看得出:
哪种写法在什么版本的 MySQL 上可能走 filesort 你写的字段有没有索引 查询是不是还能加聚合、筛选
我自己在处理大数据量的时候,踩过无数次 “GROUP BY filesort” 的坑,后来一看到 Using temporary; Using filesort
就条件反射去加索引或者改写逻辑了。
另外顺便提一句,DISTINCT
也不是没坑,它的“全字段作用”有时候会意外拉高去重复杂度。比如:
SELECT DISTINCT name, create_time FROM user;
如果 create_time 是毫秒级时间戳,几乎每条记录都不同,根本去不掉重复,结果集和原始数据几乎一样,效率反而更低。用 GROUP BY name
+ 聚合反倒更合适。
所以啊,这个问题的答案不是 “谁更快”,而是“你知道他们本质上是啥、执行计划差在哪、什么时候该用谁”。
别一上来就丢个“DISTINCT 更快”,面试官可能马上反问你“那聚合怎么算?”
你要做的是,先判断语义是不是完全一样,然后看数据量、索引、版本,再决定用哪个。顺便加上一句“我个人更喜欢用 GROUP BY,因为它更灵活”,就妥了。

優(yōu)網(wǎng)科技秉承"專業(yè)團隊、品質(zhì)服務(wù)" 的經(jīng)營理念,誠信務(wù)實的服務(wù)了近萬家客戶,成為眾多世界500強、集團和上市公司的長期合作伙伴!
優(yōu)網(wǎng)科技成立于2001年,擅長網(wǎng)站建設(shè)、網(wǎng)站與各類業(yè)務(wù)系統(tǒng)深度整合,致力于提供完善的企業(yè)互聯(lián)網(wǎng)解決方案。優(yōu)網(wǎng)科技提供PC端網(wǎng)站建設(shè)(品牌展示型、官方門戶型、營銷商務(wù)型、電子商務(wù)型、信息門戶型、微信小程序定制開發(fā)、移動端應(yīng)用(手機站、APP開發(fā))、微信定制開發(fā)(微信官網(wǎng)、微信商城、企業(yè)微信)等一系列互聯(lián)網(wǎng)應(yīng)用服務(wù)。