mysql中DISTINCT与GROUP BY的不同应用场景

1次阅读

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

mysql中DISTINCT与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

DISTINCTGROUP BY 在单字段去重时行为一致,但执行计划可能不同

SELECT DISTINCT city FROM usersSELECT 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 可能跑通,但返回的 namecreated_at 是随机某一行的值,完全不可控。正确做法是用窗口函数(MySQL 8.0+)或关联子查询。

  • 不要依赖 MySQL 的“隐式分组”行为,它已被标记为过时且默认禁用
  • DISTINCT 从不承诺返回哪一行,GROUP BY 也不该被当作“取第一行”的捷径
  • 真正需要“每组取最新/最大/最小值”时,必须显式用聚合函数(如 MAX(created_at))或 ROW_NUMBER()

实际选型就看一句话:只要去重,没聚合需求,优先 DISTINCT;一旦要算数量、求和、找最大值,或者需要按多个维度切片分析,就必须用 GROUP BY。两者的边界不在语法,而在你到底想回答什么问题。

text=ZqhQzanResources