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