必须用 HAVING 筛选重复行,因为 WHERE 无法过滤分组后的聚合结果;正确写法是 SELECT 字段, COUNT() FROM 表 GROUP BY 字段 HAVING COUNT() > 1。

为什么 COUNT(*) + GROUP BY 不能直接筛出重复行
直接写 SELECT name, COUNT(*) FROM users GROUP BY name 会返回每组的计数,但不等于“只显示重复的”。它连只出现 1 次的也列出来。真正要的是「只保留 COUNT(*) > 1 的分组」——这必须靠 HAVING,不是 WHERE。
-
WHERE在分组前过滤行,无法引用COUNT(*) -
HAVING在分组后过滤组,支持聚合函数条件 - 漏写
HAVING是新手最常踩的坑,结果查一堆单次数据还误以为逻辑错了
正确写法:GROUP BY + COUNT + HAVING 组合模板
核心结构固定:SELECT 分组字段 + 聚合函数,GROUP BY 同字段,HAVING 加条件。例如查邮箱重复:
SELECT email, COUNT(*) AS cnt FROM users GROUP BY email HAVING COUNT(*) > 1;
- 字段必须出现在
GROUP BY中(MySQL 5.7+ 严格模式下,SELECT *会报错) -
COUNT(*)和COUNT(email)在非空场景等价;但若字段含NULL,后者会忽略NULL行 - 别名
cnt不能在HAVING中直接用(如HAVING cnt > 1),多数数据库不支持——得重复写COUNT(*)
想看具体哪几条重复?用窗口函数或子查询关联
GROUP BY 只给汇总,看不到原始记录。要查出所有重复的完整行,得额外处理:
- 方法一(推荐,兼容性好):用子查询先找出重复值,再
JOIN或IN
SELECT * FROM users WHERE email IN ( SELECT email FROM users GROUP BY email HAVING COUNT(*) > 1 );
COUNT(*) OVER (PARTITION BY ...)
IN 子查询可能变慢,可改用 EXISTS 或加索引优化 email 字段多个字段联合去重计数时,GROUP BY 要写全
比如查「同名同邮箱」才算重复,GROUP BY 必须包含所有判断维度:
SELECT name, email, COUNT(*) AS cnt FROM users GROUP BY name, email HAVING COUNT(*) > 1;
- 少写一个字段(如只
GROUP BY name),就变成按姓名聚合,和业务意图不符 - 字段顺序不影响结果,但建议和
SELECT保持一致,避免阅读混淆 - 字符串字段注意是否区分大小写——MySQL 默认不区分,PostgreSQL 区分;必要时加
LOWER(email)统一处理
实际用的时候,最容易被忽略的是 NULL 值参与分组的行为:所有 NULL 会被当成同一组。如果业务上 NULL 不代表相同含义,得提前用 COALESCE(email, 'unknown') 处理。










