mysql中删除重复数据的SQL语句写法

12次阅读

用 group by+min(id) 保留每组 email 中 id 最小的记录,再通过 left join 匹配删除其余重复行;需确保 email 字段有索引,mysql 5.7 以下不支持 row_number(),操作前须备份并验证。

mysql 中删除重复数据的 SQL 语句写法

GROUP BY + MIN(id) 找出要保留的记录

MySQL 没有直接的“去重删除”语法,得先明确保留哪一条:通常选 id 最小(或最大)的那条。假设表叫 users,重复依据是 email 字段,那么先查出每个 email 对应的最小 id

SELECT MIN(id) AS keep_id, email FROM users GROUP BY email;

这个结果就是你“想留下的那些行”。下一步就是删掉其余的。

DELETE …… JOIN 一次性删掉重复行

这是最常用、效率也相对可控的方式。核心思路:把原表和刚才的分组结果做 LEFT JOIN,找出没被匹配上的那些行(即该 emailid 不是最小的),然后删掉它们:

DELETE u1 FROM users u1 LEFT JOIN (SELECT MIN(id) AS keep_id, email     FROM users     GROUP BY email ) u2 ON u1.email = u2.email AND u1.id = u2.keep_id WHERE u2.keep_id IS NULL;
  • u1 是原表别名,u2 是子查询结果别名
  • ON 条件里必须同时匹配 emailid,否则会误删
  • WHERE u2.keep_id IS NULL 表示这条 u1 记录没找到对应的“保留 ID”,该删

注意主键和索引对执行的影响

如果 email 字段没建索引,GROUP BYJOIN 都会变慢,大表可能卡住甚至超时:

  • 执行前加索引:CREATE INDEX idx_email ON users(email);
  • 若表有自增 id 主键,上面语句能用;但若没有主键或用复合唯一键,得改用 ROW_NUMBER()(仅 MySQL 8.0+ 支持)
  • MySQL 5.7 及更早版本不支持窗口函数,别写 ROW_NUMBER() OVER (PARTITION BY email ORDER BY id),会报错 ERROR 1064

误删风险高,务必先备份或用事务测试

这条 DELETE 语句不可逆。线上操作前必须:

  • 在测试库跑一遍,确认影响行数:SELECT COUNT(*) FROM users u1 LEFT JOIN (……) u2 …… WHERE u2.keep_id IS NULL;
  • 用事务包住,删完立刻 SELECT 验证:
    START TRANSACTION; DELETE u1 FROM users u1 …… ; SELECT email, COUNT(*) FROM users GROUP BY email HAVING COUNT(*) > 1;
  • 如果表很大,考虑分批删(比如按 id 范围),避免锁表太久

真正难的不是写出语句,而是确认“哪些字段才算重复”“保留逻辑是否符合业务”,比如两个 email 相同但 status 不同,该留激活的还是未激活的——这得看需求,SQL 本身不会替你判断。

text=ZqhQzanResources