批次 INSERT ... SELECT 或應用層批次 INSERT 遇到唯一索引 (主鍵/唯一鍵) 衝突,預設報 1062 錯誤並**整批回滾**。MySQL 提供三種語句級處理策略,行為差異巨大,選型需匹配業務語義。
CREATE TABLE testtable1 (
Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
UserId INT UNIQUE KEY, -- 唯一業務鍵
UserName VARCHAR(10),
UserType INT
) ENGINE=InnoDB;
CREATE TABLE testtable2 LIKE testtable1;
-- testtable1 既有資料
INSERT INTO testtable1 (UserId, UserName, UserType) VALUES
(101, 'aa', 1), (102, 'bbb', 2), (103, 'ccc', 3);
-- testtable2 待導入資料 (含衝突 UserId=101)
INSERT INTO testtable2 (UserId, UserName, UserType) VALUES
(201, 'aaa', 1), (202, 'bbb', 2), (203, 'ccc', 3), (101, 'xxxx', 5);
INSERT IGNORE INTO testtable1 (UserId, UserName, UserType)
SELECT UserId, UserName, UserType FROM testtable2;
REPLACE INTO testtable1 (UserId, UserName) -- 注意:未指定 UserType
SELECT UserId, UserName FROM testtable2;
UserType 變為 NULL (或 0,視定義)。-- 僅更新 UserName,保留 UserType
INSERT INTO testtable1 (UserId, UserName, UserType)
SELECT UserId, UserName, UserType FROM testtable2
ON DUPLICATE KEY UPDATE
UserName = VALUES(UserName);
-- UserType 不寫 → 保留舊值
testtable2.UserName。| 維度 | INSERT IGNORE | REPLACE INTO | ON DUPLICATE KEY UPDATE |
|---|---|---|---|
| 衝突行處理 | 丟棄 (保留舊) | 刪除舊 + 插入新 (全欄覆蓋) | 就地更新指定欄位 (保留其餘) |
| 未指定欄位 | 無影響 | 重置為 DEFAULT/NULL | 保留舊值 |
| 自增 ID | 衝突行消耗 ID (產生間隙) | 衝突行重用舊 ID (DELETE+INSERT) | 衝突行重用舊 ID (UPDATE) |
| 觸發器 | INSERT 觸發器 (衝突行不觸發) | DELETE + INSERT 觸發器 | UPDATE 觸發器 |
| 外鍵/引用 | 無影響 | DELETE 可能觸發 CASCADE | 無 DELETE,安全 |
| 效能 | 最快 (無更新開銷) | 較慢 (DELETE+INSERT) | 中等 (UPDATE) |
| 推薦場景 | 去重、冪等導入 | 全量覆蓋、結構簡單 | 增量同步、計數器、部分更新 |