業務系統因缺乏唯一約束、重複提交、同步邏輯缺陷,產生大量重複記錄。刪除重複數據核心邏輯是:**定義「重複鍵」 → 標記「保留行」 → 刪除其餘**。本文提供四種成熟模式,覆蓋 MySQL 5.6 到 8.0 全版本。
-- 1. 查找全部重複記錄(按 title 判重)
SELECT * FROM t_info a
WHERE (SELECT COUNT(*) FROM t_info WHERE title = a.title) > 1
ORDER BY title DESC;
-- 2. 刪除重複,保留 ID 最大(最新)的一條
DELETE FROM t_info
WHERE id NOT IN (
SELECT MAX(id) FROM t_info GROUP BY title
);
-- 3. 刪除重複,保留 ID 最小(最舊)的一條
DELETE FROM t_info
WHERE id NOT IN (
SELECT MIN(id) FROM t_info GROUP BY title
);
注意: NOT IN 子查詢返回 NULL 會導致全表刪除,務必保證子查詢無 NULL(主鍵非空天然滿足)。大表子查詢效能差,見模式二。
-- 1. 建臨時表存保留行主鍵 CREATE TEMPORARY TABLE tmp_keep AS SELECT MAX(id) AS keep_id FROM t_info GROUP BY title HAVING COUNT(*) > 1; -- 2. 刪除(用 JOIN 替代 IN,優化器更穩定) DELETE t FROM t_info t LEFT JOIN tmp_keep k ON t.id = k.keep_id WHERE k.keep_id IS NULL; -- 3. 清理 DROP TEMPORARY TABLE tmp_keep;
優勢: 避免長事務鎖表,臨時表會話隔離,不影響主從複寫(若用 ROW 格式)。
-- 查找 (peopleId, seq) 組合重複
SELECT * FROM vitae a
WHERE (a.peopleId, a.seq) IN (
SELECT peopleId, seq FROM vitae GROUP BY peopleId, seq HAVING COUNT(*) > 1
);
-- 刪除,保留 rowid 最小
DELETE a FROM vitae a
WHERE (a.peopleId, a.seq) IN (
SELECT peopleId, seq FROM vitae GROUP BY peopleId, seq HAVING COUNT(*) > 1
) AND a.rowid NOT IN (
SELECT MIN(rowid) FROM vitae GROUP BY peopleId, seq HAVING COUNT(*) > 1
);
-- 1. 標記重複行序號
WITH ranked AS (
SELECT
id,
title,
ROW_NUMBER() OVER (PARTITION BY title ORDER BY id DESC) AS rn
FROM t_info
)
-- 2. 直接刪除 rn > 1
DELETE FROM t_info
WHERE id IN (SELECT id FROM ranked WHERE rn > 1);
擴展: PARTITION BY title, category 多字段去重;ORDER BY created_at DESC 保留最新;RANK() 保留所有並列第一。
-- 快速去重並保留一行 CREATE TABLE tmp_dedup AS SELECT DISTINCT * FROM source_table; TRUNCATE TABLE source_table; INSERT INTO source_table SELECT * FROM tmp_dedup; DROP TABLE tmp_dedup; -- 根治:加唯一索引 UNIQUE KEY uk_all (col1, col2, ...)
| 模式 | 適用量級 | 優點 | 缺點 |
|---|---|---|---|
| 子查詢 IN/NOT IN | < 10 萬行 | SQL 簡單 | 大表極慢,NOT IN 陷阱 |
| 臨時表 + JOIN | 10 萬 - 1 億行 | 可控、可分批、不阻塞主從 | 需額外磁盤空間 |
| 窗口函數 (8.0+) | 任意量級 | 單掃描、語義清晰、可並行 | 需 8.0+,舊版本不可用 |
| 建表去重 | 完全重複行 | 最快 | 需鎖表、外鍵依賴需處理 |
UNIQUE INDEX,INSERT IGNORE / ON DUPLICATE KEY UPDATE。SELECT COUNT(*)-COUNT(DISTINCT uk) FROM table 異常報警。刪除重複數據是治標,**唯一約束 + 幂等設計** 才是治本。根據表量級、版本、業務窗口選擇合適模式,動手前必備份、驗證、分批,事後必加索引、審計來源。