最直接有效的方式是用GROUP BY对目标字段分组,再用HAVING COUNT(*) > 1筛选重复项;查完整行需用IN子查询或窗口函数,注意NULL处理、索引优化及事务保护。

用 GROUP BY + HAVING 查重复的行
最直接有效的方式是用 GROUP BY 对目标字段分组,再用 HAVING COUNT(*) > 1 筛出重复项。比如查 users 表中邮箱重复的记录:
SELECT email, COUNT(*) AS cnt FROM users GROUP BY email HAVING cnt > 1;
注意:不能只写 SELECT *,因为 GROUP BY 后非分组字段必须出现在聚合函数里(如 MAX(id)),否则 MySQL 8.0+ 会报错 Expression #1 of SELECT list is not in GROUP BY clause。
查出所有重复记录的完整行数据
上面只返回分组摘要,如果要看到每条重复的原始记录(比如带 id、name),得用子查询或自连接:
- 推荐子查询方式(兼容性好):
SELECT * FROM users WHERE email IN ( SELECT email FROM users GROUP BY email HAVING COUNT(*) > 1 );
- 注意:如果重复字段允许
NULL,IN会漏掉NULL值,此时改用EXISTS或单独加OR email IS NULL判断 - 大数据量时,确保
email字段有索引,否则GROUP BY和IN子查询都可能变慢
按多字段组合查重复(如 name + phone)
业务中常需判断“姓名+手机号”是否重复,这时 GROUP BY 要列出多个字段:
立即学习“PHP免费学习笔记(深入)”;
SELECT name, phone, COUNT(*) FROM contacts GROUP BY name, phone HAVING COUNT(*) > 1;
对应查完整行:
SELECT * FROM contacts WHERE (name, phone) IN ( SELECT name, phone FROM contacts GROUP BY name, phone HAVING COUNT(*) > 1 );
注意:MySQL 支持元组 (a,b) IN (SELECT a,b...),但旧版本(AND 连接;phpMyAdmin 默认用 MySQL,一般没问题,但若连的是 MariaDB 10.2 以下,建议先测试语法。
避免误删:先看再删,别直接 DELETE
很多人查到重复后手快执行 DELETE,结果删错。安全做法分三步:
- 先用
SELECT确认哪些行要留、哪些要删(比如保留MIN(id)的那条) - 用
DELETE+JOIN或子查询精准删除,例如删掉重复邮箱中id较大的行:DELETE u1 FROM users u1 INNER JOIN users u2 WHERE u1.email = u2.email AND u1.id > u2.id;
- 操作前务必在 phpMyAdmin 中点「导出」备份当前表,或确认已开启 binlog 可回滚
真正容易被忽略的是 NULL 处理和事务边界——phpMyAdmin 默认每个 SQL 是自动提交的,没开事务就无法回滚。如果要删大量重复数据,建议先在 SQL 窗口顶部勾选「启用多查询」,再手动加 BEGIN; / COMMIT; 包裹操作。



















