批次寫入資料時,主鍵或唯一鍵衝突是常見挑戰。MySQL 提供三種語法層面的處理機制:INSERT IGNORE、REPLACE INTO、INSERT ... ON DUPLICATE KEY UPDATE。本文透過實測案例深度解析三者行為差異、適用場景與效能特徵。
CREATE TABLE Tignore (ID INT NOT NULL PRIMARY KEY, NAME1 INT) DEFAULT CHARSET=utf8;
CREATE TABLE Treplace (ID INT NOT NULL PRIMARY KEY, NAME1 INT) DEFAULT CHARSET=utf8;
CREATE TABLE Tupdate (ID INT NOT NULL PRIMARY KEY, NAME1 INT UNIQUE KEY) DEFAULT CHARSET=utf8;
INSERT IGNORE INTO Tignore VALUES (1,1), (1,2), (2,2);
行為:遇到主鍵/唯一鍵衝突,跳過該行,繼續處理後續行,不報錯。
結果:僅插入 (1,1)、(2,2),第二行 (1,2) 被忽略。
適用場景:日誌寫入、冪等導入、不關心衝突行內容。
REPLACE INTO Treplace VALUES (1,1), (1,2), (2,2);
行為:衝突時先 DELETE 舊行,再 INSERT 新行。
結果:最終表內資料為 (1,2), (2,2)。
影響行數:回傳 4 rows affected(1 insert + 1 delete + 1 insert + 1 insert)。
⚠️ 關鍵風險:
DELETE + INSERT 觸發器適用場景:需以新資料完全覆蓋舊資料、且無外鍵依賴的簡單表。
INSERT INTO Tupdate VALUES (1,1), (1,2) ON DUPLICATE KEY UPDATE NAME1 = NAME1 + 1;
INSERT INTO Tupdate VALUES (1,1), (1,2) ON DUPLICATE KEY UPDATE NAME1 = VALUES(NAME1) + 1;
行為:衝突時執行 UPDATE,不刪除舊行,保留主鍵、自增 ID、外鍵關聯。
兩種寫法差異:
| 寫法 | 等效邏輯 | 結果 (NAME1 初始=1) |
|---|---|---|
NAME1 = NAME1 + 1 | UPDATE SET NAME1 = NAME1 + 1 WHERE ID=1 | 2 (累加) |
NAME1 = VALUES(NAME1) + 1 | UPDATE SET NAME1 = 2 + 1 WHERE ID=1 | 3 (基於新值計算) |
INSERT INTO Tupdate VALUES (1,1), (2,1) ON DUPLICATE KEY UPDATE NAME1 = VALUES(ID) + 1;
關鍵行為:UPDATE 針對衝突的唯一鍵對應行(此例 NAME1=1 對應 ID=1),不會插入 ID=2 的新行。最終僅更新 ID=1 行的 NAME1。
| 特性 | INSERT IGNORE | REPLACE INTO | ON DUPLICATE KEY UPDATE |
|---|---|---|---|
| 衝突處理 | 忽略/跳過 | 刪舊插新 | 就地更新 |
| 保留主鍵/自增ID | 是 | 否(可能變) | 是 |
| 觸發器 | 僅 INSERT | DELETE + INSERT | UPDATE |
| 外鍵安全 | 安全 | 風險(級聯刪除) | 安全 |
| 效能 | 最好 | 較差 | 良好 |
| 回傳影響行數 | 實際插入行數 | 刪除+插入總和 | 1=新增, 2=更新 |
三者皆支援標準 INSERT 語法變體:
INSERT INTO ... VALUES (...)INSERT INTO ... SET col=val, ...INSERT INTO ... SELECT ...INSERT IGNORE,配合 LOAD DATA INFILE ... IGNORE 效能最高。ON DUPLICATE KEY UPDATE 以「第一個衝突的唯一鍵」決定更新目標行,設計索引順序時需考慮此行為。掌握三種衝突處理機制的語義邊界與效能特徵,能針對不同業務場景(日誌寫入、資料同步、冪等 API、統計累加)選擇最適當的寫入策略,避免資料不一致與效能陷阱。