場景:需一次性更新 5000 條會員記錄(id、name、email)。傳統 UPDATE 僅支援單條,循環執行 5000 次網絡往返極慢。MySQL 獨有的 INSERT ... ON DUPLICATE KEY UPDATE 可將「不存在則插入、存在則更新」合併為單條 SQL,實現批量 Upsert。
INSERT INTO t_member (id, name, email) VALUES (1, 'nick', 'nick@126.com'), (4, 'angel', 'angel@163.com'), (7, 'brank', 'ba198@126.com') ON DUPLICATE KEY UPDATE name = VALUES(name), email = VALUES(email);
VALUES(col) 引用插入行中對應列的值,避免重複寫常量。ON CONFLICT,SQL Server 用 MERGE,Oracle 用 MERGE。| 方式 | 網絡往返 | 耗時估算 | 備註 |
|---|---|---|---|
| 循環 UPDATE | 5000 | ~15-30 秒 | 每條單獨解析、優化、執行 |
| 單條 INSERT ... ON DUPLICATE KEY UPDATE | 1 | ~0.2-0.5 秒 | 單次解析、批量執行,Redo Log 連續寫入 |
AUTO_INCREMENT 計數器仍會增長,可能導致主鍵空洞。BEFORE INSERT/AFTER INSERT 觸發器,重複鍵更新時 只會觸發 BEFORE/AFTER UPDATE,INSERT 觸發器不會再跑。max_allowed_packet (默認 4MB/64MB) 會報錯,建議每 1000-2000 行分批提交。INSERT INTO t_member (id, name, email, last_login) VALUES (1, 'nick', 'nick@126.com', NOW()), (4, 'angel', 'angel@163.com', NOW()) ON DUPLICATE KEY UPDATE last_login = VALUES(last_login), -- 僅刷新登錄時間 name = VALUES(name); -- name 同步更新
未列在 UPDATE 子句的列(如 email)將保持原值不變。
INSERT ... ON DUPLICATE KEY UPDATE 是 MySQL 批量 Upsert 的「殺手鐧」,能將萬條級更新從「分鐘級」壓縮至「毫秒級」。只要表有唯一鍵、業務允許非標準 SQL、注意分片與死鎖,即可放心用於生產環境核心寫入路徑。