COUNT(DISTINCT column_name)用于统计某列非重复值总数,支持单列或多列组合,但不可在括号内加WHERE或ORDER BY;整行去重需用SELECT COUNT() FROM (SELECT DISTINCT FROM t) AS t;注意NULL视为相同值及大小写敏感问题。

直接查非重复记录总数:用 COUNT(DISTINCT column_name)
想统计某列有多少个不同值,比如用户表里有多少个不重复的邮箱、订单表里有多少个不同用户 ID,就用 COUNT(DISTINCT column_name)。它会自动去重再计数,结果是单个整数。
注意:MySQL 5.7+ 和 8.0 都支持,但 DISTINCT 只能作用于单列或多列组合(如 COUNT(DISTINCT a, b)),不能加 WHERE 或 ORDER BY 在括号里。
- 错误写法:
COUNT(DISTINCT user_id WHERE status = 'active')—— 语法报错 - 正确替代:
COUNT(DISTINCT CASE WHEN status = 'active' THEN user_id END) - 多列去重计数:
COUNT(DISTINCT user_id, order_date)统计“用户+日期”组合的不重复对数
查整行不重复的记录数:得先 DISTINCT * 再套 COUNT()
如果目标是“整张表里真正不同的行有多少条”,比如存在重复插入导致完全相同的多行,这时不能只依赖某列——得对所有字段去重。MySQL 不允许 COUNT(DISTINCT *),必须显式写出所有列或用子查询。
推荐写法:
SELECT COUNT(*) FROM (SELECT DISTINCT * FROM your_table) AS t;
- 性能敏感时慎用:全字段
DISTINCT会触发临时表 + 文件排序,大表可能慢且占内存 - 有主键/唯一键时更高效:直接按主键去重,比如
COUNT(DISTINCT id),前提是业务上“id 唯一 = 行唯一” - 含 NULL 的列要注意:
DISTINCT把多个NULL当作相同值处理,这符合 SQL 标准,但容易被忽略
COUNT(DISTINCT) 在 GROUP BY 场景下的常见误用
很多人想“按部门分组,查每个部门有多少个不重复的职位”,却写出 GROUP BY dept; COUNT(DISTINCT job) —— 这本身没错,但容易踩两个坑:
- 没加
WHERE过滤脏数据:比如job是空字符串或全是空格,会被算作一个有效值,建议先TRIM(job) != '' - 聚合字段类型隐式转换:若
job是VARCHAR但值里混了大小写(如 'Manager' 和 'manager'),默认校对规则下它们是不同值;需要统一转小写:COUNT(DISTINCT LOWER(job)) - 超大数据量时,
COUNT(DISTINCT)在 GROUP BY 中可能触发Using temporary; Using filesort,EXPLAIN 能看到,此时考虑加复合索引(如(dept, job))
替代方案:当 COUNT(DISTINCT) 太慢或不支持时
某些旧版 MySQL(如 4.x)不支持 DISTINCT 在聚合函数中,或者你正在用严格模式遇到内存限制,可改用子查询去重:
SELECT COUNT(*) FROM (SELECT column_name FROM your_table GROUP BY column_name) AS t;
- 原理一样,但兼容性更好;缺点是不能直接用于多列,需改成
GROUP BY col1, col2 - 如果只是判断“是否存在重复”,比总数更重要,用
HAVING COUNT(*) > 1查重记录更快 - 线上大表慎用:子查询仍需扫描全表,无索引时可能锁表时间长
实际用哪一种,取决于你要去重的维度(单列?整行?带条件?)、数据规模,以及有没有现成索引支撑。最常被忽略的是 NULL 处理和大小写敏感问题,查出来数字偏大,八成卡在这儿。











