DISTINCT 是标准、简洁、高效的选择;GROUP BY 在只查唯一值时属于“杀鸡用牛刀”,除非同时需聚合计算。

查唯一值该用 DISTINCT 还是 GROUP BY
直接说结论:DISTINCT 是标准、简洁、高效的选择;GROUP BY 在只查唯一值时属于“杀鸡用牛刀”,除非你同时要聚合计算(比如统计每个值出现几次)。很多人误以为两者等价,其实语义和执行逻辑完全不同。
常见错误现象:用 GROUP BY 只写一个字段却没加聚合函数,结果看似能跑通,但一旦换数据库(比如从 MySQL 切到 PostgreSQL 或 SQL Server)就直接报错——因为严格 SQL 标准要求 SELECT 中所有非聚合字段必须出现在 GROUP BY 子句里。
-
DISTINCT是集合操作,语义清晰:“我要这一列所有不重复的值” -
GROUP BY是分组操作,语义是“按这些字段把行归成若干组,每组算一个结果”,单列无聚合时只是副作用 - 性能上,多数引擎对
DISTINCT有专门优化(如哈希去重),而GROUP BY通常触发更重的分组流程
什么时候非得用 GROUP BY 而不是 DISTINCT
当你不只是要唯一值,还要知道每个值的附加信息时。GROUP BY 才是正确工具,DISTINCT 做不到。
典型场景:查用户表中每个 country 的唯一值,同时统计人数、取最早注册时间、列出前 3 个用户名……这些都必须靠 GROUP BY + 聚合函数。
- 统计频次:
SELECT country, COUNT(*) FROM users GROUP BY country - 带条件聚合:
SELECT status, AVG(amount) FROM orders GROUP BY status HAVING AVG(amount) > 100 - 多字段去重并取最新记录(需配合窗口函数或子查询):
GROUP BY user_id配合MAX(created_at)是常见起点
注意:HAVING 必须跟在 GROUP BY 后,不能用于 DISTINCT 查询——这是语法硬限制。
DISTINCT 的常见坑与绕过方法
DISTINCT 看似简单,但实际用错频率很高。核心问题是它作用于整行,不是单列——哪怕你只写了一个字段,底层仍是先取所有匹配行、再对整行做去重。
- 想查
name唯一值,但表里有id,name,email三列,写SELECT DISTINCT name FROM t没问题;但若写SELECT DISTINCT name, email FROM t,得到的是“name+email”组合唯一,不是name单独唯一 -
NULL被视为相同值:所有NULL会被合并成一条,这是标准行为,但容易被忽略 - 性能隐患:如果列上有大量重复值,
DISTINCT仍需扫描全表;加索引对去重本身帮助有限,但可加速WHERE过滤阶段 - 想排除
NULL再去重?加WHERE column IS NOT NULL,别指望DISTINCT自动过滤
MySQL 和 PostgreSQL 对 GROUP BY 的宽容度差异
MySQL 5.7 默认开启 ONLY_FULL_GROUP_BY 之前,允许 SELECT a, b FROM t GROUP BY a(b 不在 GROUP BY 里也不聚合),结果是返回每组第一条的 b 值——这既不标准也不可预测。PostgreSQL、SQL Server、Oracle 一律拒绝这种写法。
这意味着:依赖 MySQL “宽松模式” 写的 GROUP BY 查询,迁移到其他数据库大概率失败。
- 安全写法永远是:所有非聚合字段必须显式出现在
GROUP BY - 如果真只需要唯一值,别碰
GROUP BY——省事、可移植、不易出错 - 检查当前 MySQL 模式:
SELECT @@sql_mode,确认是否含ONLY_FULL_GROUP_BY
最易被忽略的一点:即使你只想要唯一值,如果后续要加排序或分页,DISTINCT 和 GROUP BY 的执行顺序、索引利用方式也不同,可能导致分页结果不稳定——尤其在高并发更新场景下。

















