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

