業務需一次性更新成百上千條記錄,每條記錄更新值不同。循環執行單條 UPDATE 會導致:網絡往返開銷大、事務頻繁提交、鎖競爭激烈、執行時間呈線性增長。
原理: 利用 CASE WHEN 在單條 SQL 中構建「ID -> 新值」映射,配合 WHERE id IN (...) 限制掃描範圍。
UPDATE categories
SET display_order = CASE id
WHEN 1 THEN 3
WHEN 2 THEN 4
WHEN 3 THEN 5
END,
title = CASE id
WHEN 1 THEN 'New Title 1'
WHEN 2 THEN 'New Title 2'
WHEN 3 THEN 'New Title 3'
END
WHERE id IN (1,2,3);
PHP 構建示例:
$data = [1=>3, 2=>4, 3=>5]; // id => new_value
$ids = implode(',', array_keys($data));
$sql = "UPDATE categories SET display_order = CASE id ";
foreach ($data as $id => $val) {
$sql .= sprintf("WHEN %d THEN %d ", $id, $val);
}
$sql .= "END WHERE id IN ($ids)";
// 執行 $sql
優點: 單次交互、原子性、語法簡單。
缺點: SQL 長度受 max_allowed_packet 限制 (默認 4MB/64MB),超大批次需分片;更新列多時 SQL 膨脹明顯。
前提: 表有 主鍵或唯一鍵。
原理: 嘗試插入,主鍵衝突則轉為更新,MySQL 內部優化為單次掃描、單次加鎖。
INSERT INTO test_tbl (id, dr) VALUES (1,'2'), (2,'3'), ..., (10000,'x') ON DUPLICATE KEY UPDATE dr = VALUES(dr);
對比 REPLACE INTO:
| 特性 | REPLACE INTO | INSERT ... ON DUPLICATE KEY UPDATE |
|---|---|---|
| 機制 | DELETE + INSERT | 原地 UPDATE |
| 非更新列 | 重置為默認值 | 保持原值不變 |
| 自增 ID | 可能改變 | 不變 |
| 觸發器 | 觸發 DELETE/INSERT | 觸發 UPDATE |
結論: 絕大多數場景首選 ON DUPLICATE KEY UPDATE,數據完整性更強。
-- 1. 創建臨時表 (需 CREATE TEMPORARY TABLES 權限) CREATE TEMPORARY TABLE tmp_upd (id INT PRIMARY KEY, dr VARCHAR(50)); -- 2. 批量導入數據 (LOAD DATA INFILE 極快) LOAD DATA LOCAL INFILE '/path/data.csv' INTO TABLE tmp_upd FIELDS TERMINATED BY ','; -- 或 INSERT INTO tmp_upd VALUES ... (分批提交) -- 3. 多表關聯更新 UPDATE test_tbl t JOIN tmp_upd tmp ON t.id = tmp.id SET t.dr = tmp.dr; -- 4. 會話結束自動清理臨時表
優勢: 解耦數據導入與更新邏輯,LOAD DATA 導入速度遠超 INSERT,適合 10 萬+ 行級批量更新。
| 方案 | Real Time | User CPU | Sys CPU | 適用場景 |
|---|---|---|---|---|
| 循環單條 UPDATE | 15.5s | 1.68s | 1.37s | 極小批量 (<100)、邏輯極其複雜 |
| REPLACE INTO | 1.39s | 0.06s | 0.01s | 需重置非更新列、無外鍵依賴 |
| INSERT ON DUPLICATE KEY UPDATE | 1.47s | 0.05s | 0.01s | 通用首選,性能優、語義正確 |
| 臨時表 + JOIN UPDATE | 0.64s | 0.06s | 0.00s | 超大批量、配合 LOAD DATA、需臨時表權限 |
CASE WHEN 單語句,代碼簡潔。INSERT ... ON DUPLICATE KEY UPDATE,分批 1000 行提交,事務包裹。LOAD DATA -> 臨時表 -> JOIN UPDATE,極致吞吐。