MySQL 表里出现重复数据,是很多业务都会遇到的问题。比如用户重复注册、商品重复导入、订单外部编号重复、日志重复写入、采集数据重复入库等。如果不及时处理,后续统计、查询和业务判断都会受到影响。
数据去重不能一上来就删除。正确流程应该是先确认重复规则,再查询重复记录,备份数据,最后选择保留哪一条并批量删除。本文从重复记录查询讲起,再整理保留一条和防止再次重复的方法。
先明确去重规则
去重前必须明确:按哪个字段判断重复?是单字段重复,还是多个字段组合重复?保留最早一条,还是保留最新一条?
比如用户表可能按邮箱去重,订单表可能按外部订单号去重,商品表可能按店铺 ID 和商品编码共同去重。规则不明确,删除就很危险。
查询重复值
可以使用 GROUP BY 和 HAVING 查询重复字段。
SELECT email, COUNT(*) AS total
FROM users
GROUP BY email
HAVING total > 1;
这能找出哪些邮箱出现了多次,但还看不到具体重复记录。下一步要查询这些重复值对应的完整数据。

查看重复记录
查出重复值后,可以关联原表查看完整记录。
SELECT * FROM users
WHERE email IN (
SELECT email FROM (
SELECT email
FROM users
GROUP BY email
HAVING COUNT(*) > 1
) t
)
ORDER BY email, id;
这里多套一层子查询,是为了避免某些 MySQL 场景下对子查询的限制。查看完整记录后,再决定保留策略。
按多个字段去重
如果重复规则由多个字段组成,可以在 GROUP BY 中写多个字段。
SELECT shop_id, sku, COUNT(*) AS total
FROM products
GROUP BY shop_id, sku
HAVING total > 1;
多字段去重更符合真实业务。比如同一个 sku 在不同店铺可以重复,但同一店铺内不能重复。
保留最小 ID
最常见的去重策略是保留每组中 ID 最小的一条,删除其他重复记录。
SELECT MIN(id) AS keep_id
FROM users
GROUP BY email;
保留最小 ID 通常表示保留最早创建的数据。但如果后来的记录更新更完整,就可能应该保留最大 ID 或按更新时间选择。
删除重复数据
保留最小 ID 并删除其他记录,可以使用自连接删除。
DELETE u1 FROM users u1
JOIN users u2
ON u1.email = u2.email
AND u1.id > u2.id;
这条 SQL 的意思是:同一个 email 下,如果 u1 的 id 更大,就删除 u1,保留较小 id。执行前一定要先用 SELECT 模拟确认。
先 SELECT 再 DELETE
任何批量删除前,都应该先把 DELETE 改写成 SELECT 看看会删哪些记录。
SELECT u1.* FROM users u1
JOIN users u2
ON u1.email = u2.email
AND u1.id > u2.id;
确认结果无误后,再执行 DELETE。重要数据还要先备份,不能直接在生产库上盲删。
保留最新记录
如果希望保留最新记录,可以把条件反过来,删除 id 更小的记录。
DELETE u1 FROM users u1
JOIN users u2
ON u1.email = u2.email
AND u1.id < u2.id;
如果最新不等于最大 ID,而是按更新时间判断,就应该使用 updated_at 或业务时间字段设计规则。
使用临时表
复杂去重场景可以先把要保留的 ID 写入临时表,再删除不在保留名单里的记录。这样步骤更清晰,也便于人工检查。
CREATE TEMPORARY TABLE keep_users AS
SELECT MIN(id) AS id
FROM users
GROUP BY email;
然后再根据临时表删除重复数据。对生产数据来说,分步骤比一条复杂 SQL 更容易控制风险。
批量删除控制
如果重复数据很多,不建议一次性删除几十万行。可以分批删除,降低锁表、复制延迟和业务抖动风险。
DELETE FROM users
WHERE id IN (...)
LIMIT 1000;
实际批量清理时,要结合业务低峰期、备份和监控一起执行。
防止再次重复
清理完重复数据后,应该从源头防止再次出现。最有效的方法是增加唯一索引或联合唯一索引。
ALTER TABLE users ADD UNIQUE KEY uniq_email (email);
如果是多字段唯一,则建立联合唯一索引。
ALTER TABLE products ADD UNIQUE KEY uniq_shop_sku (shop_id, sku);
插入时处理冲突
有唯一索引后,插入重复数据会失败。可以根据业务使用 INSERT IGNORE 或 ON DUPLICATE KEY UPDATE。
INSERT INTO users (email, name)
VALUES ('demo@example.com', 'Demo')
ON DUPLICATE KEY UPDATE name = VALUES(name);
这类写法要谨慎使用,明确是忽略重复还是更新已有记录。
常见错误
第一种错误是没明确重复规则就删除。第二种错误是没有备份。第三种错误是直接 DELETE,不先 SELECT 确认。第四种错误是清理后不加唯一约束,重复数据很快又出现。第五种错误是一次删除太多导致业务抖动。
实践建议
MySQL 数据去重建议按流程来:定义重复规则,查询重复分组,查看完整重复记录,确认保留策略,备份数据,先 SELECT 验证,再分批 DELETE,最后增加唯一索引或导入约束。
去重不是单纯写一条删除 SQL,而是一次数据治理动作。安全、可回滚、能防止复发,才算真正处理完成。













暂无评论内容