必须用having不能用where,因count()是聚合函数,where在分组前执行无法访问计数结果;正确写法为group by后接having count(*)>1,可引用别名(mysql 8.0+支持),需注意null处理、索引优化及完整行需join回查。

用 HAVING COUNT > 1 找出重复分组
直接上结论:必须用 HAVING,不能用 WHERE。因为 COUNT() 是聚合函数,WHERE 在分组前执行,根本看不到分组后的计数结果。
HAVING 和 WHERE 混用时的常见错误
典型错误是把过滤条件写在 WHERE 里,比如想查“订单数超过 5 的客户”,却写成:
SELECT customer_id, COUNT(*) FROM orders WHERE COUNT(*) > 5 GROUP BY customer_id;
这会报错:ERROR: aggregate functions are not allowed in WHERE。原因很实在——WHERE 还没到分组那步,COUNT(*) 根本没算出来。
正确写法是:
SELECT customer_id, COUNT(*) AS cnt FROM orders GROUP BY customer_id HAVING COUNT(*) > 5;
-
HAVING必须跟在GROUP BY后面 -
HAVING可以引用SELECT中的别名(如HAVING cnt > 5),但不是所有数据库都支持(PostgreSQL 支持,MySQL 5.7 默认不支持,8.0+ 支持) - 如果同时有
WHERE和HAVING,WHERE先筛行,HAVING再筛组——比如先排除测试订单(WHERE status != 'test'),再找真实订单超 3 笔的客户
只查重复值本身,不要计数字段
有时你只需要重复的 email 列表,不关心具体几条,可以省略 COUNT(*) 显示:
SELECT email FROM users GROUP BY email HAVING COUNT(*) > 1;
注意点:
- 这个语句返回的是去重后的重复
email值,每值一行 - 如果还想看这些重复邮箱对应的完整记录(比如所有用户 ID),得用子查询或
JOIN,不能只靠GROUP BY + HAVING - MySQL 严格模式下,
SELECT列表中若出现未在GROUP BY中的非聚合字段(如SELECT email, user_id),会报错;PostgreSQL 更严格,直接不允许
性能和索引注意事项
HAVING COUNT() > 1 本身不走索引,它依赖分组扫描。实际慢往往是因为没限制数据量就硬 GROUP:
- 先用
WHERE尽量缩小范围(比如加时间范围、状态条件) - 确保
GROUP BY字段上有索引(如对email建索引能加速分组) - 避免在
HAVING里用函数,比如HAVING UPPER(email) > 1会让索引失效 - 如果只是查“是否存在重复”,用
EXISTS子查询可能更快(尤其大数据量时)
真正容易被忽略的是:分组键含 NULL 时,GROUP BY 会把所有 NULL 归为一组,COUNT(*) 也会把它当有效组统计——如果你业务里 email IS NULL 是合法空值,得额外加 HAVING COUNT(*) > 1 AND email IS NOT NULL 排除干扰。










