结论:必须用GROUP BY分组+COUNT()计数+HAVING过滤,三者缺一不可;WHERE在分组前执行,无法访问COUNT()结果,故WHERE COUNT(*)>1会报错,而HAVING专用于过滤分组后的聚合结果。

直接说结论:用 GROUP BY 分组 + COUNT(*) 计数 + HAVING 过滤,三者缺一不可;漏掉 HAVING 而用 WHERE 是最常见错误。
为什么不能用 WHERE 筛选重复次数?
WHERE 在分组前执行,此时 COUNT(*) 还没算出来,所以写 WHERE COUNT(*) > 1 会报错:ERROR: aggregate functions are not allowed in WHERE。必须等分组聚合完成,才能对聚合结果做判断——这正是 HAVING 的职责。
-
WHERE过滤的是「原始行」,比如WHERE status = 'active' -
HAVING过滤的是「分组后的聚合行」,比如HAVING COUNT(*) > 1 - 如果既要过滤原始行又要筛重复,两个子句要同时用:
WHERE写在前,HAVING写在后
统计每个分类下重复数据的完整写法
假设表 orders 有字段 category 和 order_id,想找出哪些 category 出现了不止一次:
SELECT category, COUNT(*) AS cnt FROM orders GROUP BY category HAVING COUNT(*) > 1;
注意点:
-
SELECT中所有非聚合字段(如category)都必须出现在GROUP BY列表里,否则 MySQL 8.0+ 或 PostgreSQL 会报错 -
COUNT(*)和COUNT(category)行为不同:后者会忽略NULL值,前者不会 - 如果只想看重复次数本身,
SELECT COUNT(*)不加其他字段也合法,但通常需要知道是哪个分类重复了
查“完全重复行”(多字段组合重复)怎么办?
比如要找 (user_id, product_id) 组合重复的记录,不能只按一个字段分组:
SELECT user_id, product_id, COUNT(*) AS cnt FROM purchases GROUP BY user_id, product_id HAVING COUNT(*) > 1;
关键差异:
-
GROUP BY后跟多个字段,用逗号分隔,表示按组合值分组 - 若误写成
GROUP BY user_id,就只统计每个用户买了几次,失去产品维度 - 某些场景需去重计数,可用
COUNT(DISTINCT order_id),但注意它不能替代分组逻辑
性能和可读性容易被忽略的细节
当数据量大时,GROUP BY 可能变慢,尤其没索引时:
- 对
GROUP BY中的字段建联合索引(如INDEX(category)或INDEX(user_id, product_id))能显著提速 - 避免在
GROUP BY中用函数,如GROUP BY UPPER(category),会导致索引失效 -
HAVING COUNT(*) > 1比HAVING COUNT(*) >= 2更符合语义直觉,但两者等价;别写成>= 1——那等于没过滤
真正麻烦的不是语法,而是分清「按什么分组」「数什么」「筛什么结果」——这三个动作一旦错位,要么查不出重复,要么查出一堆空组。

















