1. 为什么我们需要删除重复数据
在日常数据库维护中,数据重复是个令人头疼的常见问题。我刚接手公司订单系统时就发现,由于前端表单重复提交和ETL流程缺陷,订单表里有超过15%的重复记录。这不仅浪费存储空间,更会导致统计报表失真、业务逻辑错乱。
关键提示:重复数据通常分为两种——完全重复(所有字段值相同)和业务重复(关键业务字段相同但其他字段可能不同)
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 完全重复记录的删除方案
2.1 使用DISTINCT创建新表
最直观的方法是创建不含重复数据的新表:
sql复制CREATE TABLE orders_new AS
SELECT DISTINCT * FROM orders;
这种方法简单但存在明显缺陷:
- 需要额外存储空间
- 不保留原表索引和约束
- 需要手动处理外键关系
2.2 使用临时表交换数据
更专业的做法是:
sql复制-- 创建临时表并去重
CREATE TABLE temp_orders AS
SELECT * FROM orders GROUP BY order_id, customer_id;
-- 重命名交换
RENAME TABLE orders TO orders_backup, temp_orders TO orders;
我在电商项目中实测,处理200万条记录耗时约3分钟,比直接DELETE快5倍。
3. 业务重复记录的智能处理
3.1 使用窗口函数保留最新记录
当需要根据业务规则保留特定记录时(如保留最新修改的记录):
sql复制DELETE FROM orders
WHERE id NOT IN (
SELECT id FROM (
SELECT id,
ROW_NUMBER() OVER (PARTITION BY order_no ORDER BY update_time DESC) AS rn
FROM orders
) t WHERE rn = 1
);
这个方案在银行交易系统中特别实用,能确保每个交易流水号只保留最终状态。
3.2 使用自连接删除重复项
对于没有唯一ID的表,可以采用自连接:
sql复制DELETE o1 FROM orders o1
INNER JOIN orders o2
WHERE o1.create_time < o2.create_time
AND o1.order_no = o2.order_no;
4. 高级去重技巧与性能优化
4.1 大数据量分批处理
处理千万级数据时,我推荐分批提交:
sql复制-- 每次处理10万条
SET @batch_size = 100000;
SET @processed = 1;
WHILE @processed > 0 DO
DELETE FROM orders
WHERE id IN (
SELECT id FROM (
SELECT id FROM orders
WHERE order_no IN (
SELECT order_no FROM orders
GROUP BY order_no
HAVING COUNT(*) > 1
)
ORDER BY update_time DESC
LIMIT @batch_size
) t
);
SET @processed = ROW_COUNT();
COMMIT;
END WHILE;
4.2 利用索引加速去重
为关键字段创建复合索引能显著提升性能:
sql复制CREATE INDEX idx_order_unique ON orders(order_no, update_time);
5. 不同数据库的特殊语法
5.1 MySQL的DELETE JOIN语法
sql复制DELETE t1 FROM orders t1
INNER JOIN orders t2
WHERE t1.id < t2.id
AND t1.order_no = t2.order_no;
5.2 SQL Server的CTE方法
sql复制WITH CTE AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY order_no ORDER BY update_time DESC) AS rn
FROM orders
)
DELETE FROM CTE WHERE rn > 1;
5.3 Oracle的ROWID方案
sql复制DELETE FROM orders
WHERE ROWID NOT IN (
SELECT MIN(ROWID)
FROM orders
GROUP BY order_no
);
6. 实战避坑指南
-
事务管理:务必使用事务包裹删除操作,出错时可回滚
sql复制BEGIN TRANSACTION; -- 执行删除语句 COMMIT; -
备份优先:执行前先备份数据
sql复制CREATE TABLE orders_backup AS SELECT * FROM orders; -
验证SQL:先用SELECT测试会删除哪些记录
sql复制-- 先查询确认 SELECT COUNT(*) FROM orders WHERE order_no IN ( SELECT order_no FROM orders GROUP BY order_no HAVING COUNT(*) > 1 ); -
锁表问题:大表删除可能锁表,建议在低峰期操作
-
外键约束:处理有外键关联的表时,需要先禁用约束或级联删除
7. 自动化监控方案
为防止重复数据再次产生,我建议配置定期检查任务:
sql复制-- 创建监控存储过程
CREATE PROCEDURE check_duplicate_orders()
BEGIN
DECLARE duplicate_count INT;
SELECT COUNT(*) INTO duplicate_count
FROM (
SELECT order_no FROM orders
GROUP BY order_no
HAVING COUNT(*) > 1
) t;
IF duplicate_count > 0 THEN
-- 发送告警邮件
CALL send_alert_email('订单表发现重复数据', duplicate_count);
END IF;
END;
-- 创建定时任务
CREATE EVENT check_duplicates_event
ON SCHEDULE EVERY 1 DAY
DO CALL check_duplicate_orders();
这套方案在我们生产环境运行三年,成功将数据重复率控制在0.1%以下。关键在于建立预防机制而不仅是事后处理。
