MySQL處理重復數據完整代碼實例
前言
在數據庫管理中,重復數據是最常見的 “隱形殺手” 之一。它不僅會浪費存儲空間、拖慢查詢速度,還可能導致業(yè)務邏輯混亂(如統(tǒng)計結果失真、訂單重復處理)。
一、認識重復數據:從 “什么是重復” 開始
重復數據并非僅指完全相同的記錄,在實際業(yè)務中可分為兩類:
- 完全重復:所有字段值均相同的記錄(如誤操作導致的重復插入)。
- 部分重復:核心字段相同但非關鍵字段不同的記錄(如同一用戶的重復注冊,僅注冊時間不同)。
二、檢測重復數據:
1. 檢測完全重復記錄
-- 查找完全重復的記錄 SELECT * FROM table_name WHERE (col1, col2, ..., coln) IN ( SELECT col1, col2, ..., coln FROM table_name GROUP BY col1, col2, ..., coln HAVING COUNT(*) > 1 );
適用于所有字段均需唯一的場景(如配置表、字典表):
示例:檢測user_config表中完全重復的配置記錄:
SELECT * FROM user_config WHERE (user_id, config_key, config_value) IN ( SELECT user_id, config_key, config_value FROM user_config GROUP BY user_id, config_key, config_value HAVING COUNT(*) > 1 );
2. 檢測部分重復記錄(按核心字段)
適用于僅需保證核心字段唯一的場景(如用戶表的手機號、訂單表的訂單號):
-- 按核心字段分組,查找重復記錄 SELECT core_col1, core_col2, COUNT(*) AS duplicate_count FROM table_name GROUP BY core_col1, core_col2 HAVING COUNT(*) > 1;
示例:檢測users表中重復的手機號(核心字段為phone):
-- 查看重復手機號及重復次數 SELECT phone, COUNT(*) AS duplicate_count FROM users GROUP BY phone HAVING COUNT(*) > 1; -- 查看重復手機號對應的完整記錄 SELECT * FROM users WHERE phone IN ( SELECT phone FROM users GROUP BY phone HAVING COUNT(*) > 1 ) ORDER BY phone;
3. 高級檢測:帶條件的重復記錄
結合業(yè)務邏輯篩選重復記錄(如重復且狀態(tài)有效的訂單):
-- 查找狀態(tài)為"已支付"的重復訂單 SELECT order_no, COUNT(*) AS duplicate_count FROM orders WHERE status = 'PAID' GROUP BY order_no HAVING COUNT(*) > 1;
4. 使用窗口函數標記重復記錄(MySQL 8.0+)
通過ROW_NUMBER()為重復記錄編號,便于后續(xù)處理:
-- 為重復手機號的記錄編號(按注冊時間排序) SELECT id, phone, register_time, ROW_NUMBER() OVER (PARTITION BY phone ORDER BY register_time) AS rn FROM users;
- 結果中rn > 1的記錄即為需要處理的重復數據。
三、刪除重復數據:保留有效記錄
刪除重復數據的核心原則是:保留一條有效記錄(如最新 / 最早的記錄),刪除其余重復項。以下是 4 種實用方法:
1. 帶唯一標識的重復記錄刪除(推薦)
若表中有自增主鍵(如id),可通過子查詢定位并刪除重復記錄:
-- 保留重復手機號中id最小的記錄(即最早插入的記錄) DELETE FROM users WHERE id NOT IN ( SELECT min_id FROM ( -- 子查詢嵌套避免"不能從同表查詢并刪除"的限制 SELECT MIN(id) AS min_id FROM users GROUP BY phone HAVING COUNT(*) > 1 ) AS temp );
邏輯解析:
- 內層子查詢找出每組重復記錄中的最小id(要保留的記錄)。
- 外層刪除所有id不在保留列表中的記錄。
2. 無唯一標識的重復記錄刪除
若表無主鍵,可通過所有字段組合定位重復記錄:
-- 保留完全重復記錄中一條(需指定所有字段) DELETE t1 FROM table_name t1 JOIN table_name t2 ON t1.col1 = t2.col1 AND t1.col2 = t2.col2 AND ... AND t1.coln = t2.coln WHERE t1.ctid < t2.ctid; -- 利用隱藏列ctid區(qū)分物理位置(僅InnoDB有效)
3. 按條件保留記錄(如最新記錄)
通過排序保留指定條件的記錄(如最新注冊的用戶):
-- 保留重復手機號中注冊時間最新的記錄 DELETE t1 FROM users t1 JOIN users t2 ON t1.phone = t2.phone AND t1.register_time < t2.register_time; -- t1為舊記錄
4. 批量刪除大表重復數據(性能優(yōu)化)
當表數據量超過 100 萬行時,直接刪除可能導致鎖表,建議分批次處理:
-- 每次刪除1000條重復記錄(循環(huán)執(zhí)行至無重復) DELETE FROM users WHERE id IN ( SELECT id FROM ( SELECT id FROM ( SELECT id, ROW_NUMBER() OVER (PARTITION BY phone ORDER BY register_time) AS rn FROM users ) AS t WHERE rn > 1 LIMIT 1000 -- 限制單次刪除數量 ) AS temp );
注意事項:
- 刪除前必須備份:執(zhí)行CREATE TABLE users_backup AS SELECT * FROM users;創(chuàng)建備份表。
- 使用事務:通過BEGIN; DELETE ...; COMMIT;確保刪除可回滾。
- 刪除后優(yōu)化:執(zhí)行OPTIMIZE TABLE users;回收碎片空間。
四、預防重復數據:從源頭阻斷
處理重復數據的最佳方式是提前預防,以下是 3 種核心手段:
1. 建立唯一約束(最有效)
通過唯一索引或主鍵強制核心字段唯一:
-- 為手機號添加唯一索引,阻止重復插入 CREATE UNIQUE INDEX uk_users_phone ON users(phone); -- 復合唯一索引(如同一用戶的配置鍵唯一) CREATE UNIQUE INDEX uk_user_config ON user_config(user_id, config_key);
- 插入重復數據時,MySQL 會直接報錯(Duplicate entry),避免污染數據。
2. 插入時處理重復數據
通過INSERT ... IGNORE或REPLACE INTO在插入階段處理重復:
-- 插入時忽略重復記錄(不報錯,返回警告)
INSERT IGNORE INTO users (phone, name)
VALUES ('13800138000', '張三');
-- 重復時替換舊記錄(刪除舊記錄后插入新記錄)
REPLACE INTO users (phone, name)
VALUES ('13800138000', '張三');- 適用場景:數據同步、批量導入等可能產生重復的場景。
3. 業(yè)務層控制
在應用程序中添加重復校驗邏輯:
// Java示例:插入前檢查手機號是否已存在
public boolean addUser(User user) {
// 先查詢是否存在重復手機號
if (userDao.existsByPhone(user.getPhone())) {
throw new DuplicateException("手機號已注冊");
}
return userDao.insert(user) > 0;
}- 配合數據庫唯一索引,形成 “雙重保障”。
總結
到此這篇關于MySQL處理重復數據的文章就介紹到這了,更多相關MySQL處理重復數據內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

