优先用 distinct 实现简单去重,如 select distinct city from users;需聚合计算(如 count、sum)或多维分组时必须用 group by。

什么时候该用 DISTINCT 而不是 GROUP BY
DISTINCT 的核心作用是去重,它不改变行结构,只对结果集整体做唯一性过滤。适合你只需要“列出所有不同的值”这种简单需求。
比如查所有出现过的城市名:
SELECT DISTINCT city FROM users;这里没有聚合、没有分组逻辑,只是把重复的
city 值压成一个。如果加了其他字段(如 name),DISTINCT 是对整行去重,不是单看某个字段——这点常被误读。
- 不能配合聚合函数(如
COUNT()、SUM())直接使用,除非嵌套子查询 - 不能指定“按某列去重,但取其他列任意一行”,MySQL 不支持
DISTINCT ON这种 PostgreSQL 语法 - 性能上,
DISTINCT通常比GROUP BY轻量,尤其无索引时,因为不涉及分组计算和临时表构建
为什么 GROUP BY 不只是“分组”,而是“分组+聚合”的绑定操作
GROUP BY 的语义本质是“将行划分为若干组,每组输出一行”。它天然要求:非分组字段必须出现在聚合函数中,否则 MySQL 8.0+ 默认报错(ONLY_FULL_GROUP_BY 开启时)。这不是限制,而是防止语义歧义。
例如统计每个城市的用户数:
SELECT city, COUNT(*) FROM users GROUP BY city;这里
city 是分组键,COUNT(*) 是聚合结果。如果你写成 SELECT city, name FROM users GROUP BY city,MySQL 不知道该选哪一行的 name,所以会拒绝执行(除非关掉 SQL 模式,但结果不可靠)。
-
GROUP BY支持多字段组合分组,也支持表达式(如GROUP BY YEAR(created_at)) - 可以配合
HAVING对分组后结果过滤,而WHERE只能过滤分组前的行 - 在有索引时,
GROUP BY可能利用索引避免排序,但若无法走索引,会建临时表 + 文件排序,代价明显高于DISTINCT
DISTINCT 和 GROUP BY 在单字段去重时行为一致,但执行计划可能不同
像 SELECT DISTINCT city FROM users 和 SELECT city FROM users GROUP BY city,语义等价,结果一样。但优化器处理方式不同:
- MySQL 5.7 及以前,
DISTINCT内部常转为GROUP BY执行;MySQL 8.0+ 更倾向保留原意,DISTINCT可能走哈希去重,GROUP BY更可能触发排序 - 如果
city有索引,GROUP BY city可能利用索引完成分组(Using index for group-by),而DISTINCT同样受益,但不保证 - 当字段允许
NULL,两者都把所有NULL视为同一组/同一个值,行为一致
容易踩的坑:用 GROUP BY 模拟 DISTINCT 却忽略非确定性
有人为了“取每个城市最新一条用户记录”,错误地写:
SELECT city, name, created_at FROM users GROUP BY city;这在旧版本 MySQL 可能跑通,但返回的
name 和 created_at 是随机某一行的值,完全不可控。正确做法是用窗口函数(MySQL 8.0+)或关联子查询。
- 不要依赖 MySQL 的“隐式分组”行为,它已被标记为过时且默认禁用
-
DISTINCT从不承诺返回哪一行,GROUP BY也不该被当作“取第一行”的捷径 - 真正需要“每组取最新/最大/最小值”时,必须显式用聚合函数(如
MAX(created_at))或ROW_NUMBER()
DISTINCT;一旦要算数量、求和、找最大值,或者需要按多个维度切片分析,就必须用 GROUP BY。两者的边界不在语法,而在你到底想回答什么问题。










