用group by统计重复值需先按目标字段分组,再用having筛选count(*)>1的结果;where不能用于聚合过滤,多字段重复须全列出group by,注意null处理和索引优化。

怎么用 GROUP BY 找出重复的值
重复数据统计的核心不是“找出来”,而是“按什么分组后数量大于 1”。GROUP BY 是唯一能聚合计数的起点,漏掉它就等于没开始。
常见错误是直接写 SELECT name FROM users WHERE COUNT(*) > 1 —— 这会报错:ERROR: aggregate functions are not allowed in WHERE。因为 WHERE 在分组前执行,根本看不到 COUNT() 的结果。
- 必须先用
GROUP BY按目标字段分组(比如email、phone) - 再用
COUNT(*)算每组行数 - 最后用
HAVING筛选分组后的结果(不是WHERE)
示例:查所有重复的邮箱
SELECT email, COUNT(*) AS cnt FROM users GROUP BY email HAVING COUNT(*) > 1;
HAVING 和 WHERE 到底该用哪个
HAVING 是专为聚合结果筛选设计的,WHERE 只能过滤原始行。混淆两者是新手最常踩的坑,而且错误往往不报语法错,只导致逻辑错——比如本该返回 5 条重复记录,结果一条都没有。
-
WHERE放在GROUP BY前:过滤单行数据(如WHERE status = 'active') -
HAVING放在GROUP BY后:过滤分组结果(如HAVING COUNT(*) >= 3) - 不能在
HAVING里引用未出现在GROUP BY中的非聚合字段(例如GROUP BY email后,HAVING name = 'Alice'会报错)
统计多个字段组合是否重复
现实里重复往往不是单字段决定的,比如订单表中 (user_id, product_id, order_date) 组合重复才代表异常下单。
这时 GROUP BY 必须列出全部字段,顺序无关,但缺一不可。少写一个,分组粒度就变粗,可能把不该合并的行强行归到一组。
- 正确写法:
GROUP BY user_id, product_id, order_date - 错误写法:
GROUP BY user_id, product_id(会把同用户同商品不同日期的也当重复) - 注意 NULL 值:多字段组合中任一为
NULL,整组会被视为相同(多数数据库行为),如果业务上NULL有明确含义,得提前用COALESCE处理
示例:查同一用户同一天对同一商品的重复下单
SELECT user_id, product_id, order_date, COUNT(*) AS dup_cnt FROM orders GROUP BY user_id, product_id, order_date HAVING COUNT(*) > 1;
性能和兼容性要注意什么
大表跑重复统计很容易慢,尤其没索引时。数据库要全表扫描 + 排序 + 分组,三重开销。
- 给
GROUP BY字段建联合索引能显著提速(如CREATE INDEX idx_email ON users(email);) - MySQL 5.7+ 和 PostgreSQL 支持在
HAVING中复用别名(如HAVING cnt > 1),但 SQLite 和旧版 MySQL 不支持,建议始终写完整表达式HAVING COUNT(*) > 1 - 如果只要知道“有没有重复”,不用具体分布,加
LIMIT 1可提前终止(但注意:这不能代替完整统计)
真正难的不是写出语句,是想清楚“重复”在业务里到底指什么——是完全相同的行?还是关键字段组合相同?还是忽略大小写或空格后的逻辑重复?这些得靠人工定义,SQL 只负责执行。










