MySQL 批量更新不同值:從循環 UPDATE 到單語句 CASE WHEN
問題:循環 UPDATE 效能極差
foreach ($display_order as $id => $ordinal) {
$sql = "UPDATE categories SET display_order = $ordinal WHERE id = $id";
mysql_query($sql); -- N 條記錄 = N 次網絡往返 + N 次解析優化
}
解法:CASE WHEN 單語句批量更新
UPDATE categories
SET display_order = CASE id
WHEN 1 THEN 3
WHEN 2 THEN 4
WHEN 3 THEN 5
END
WHERE id IN (1,2,3);
- 單次解析、單次網絡往返、單次事務
WHERE id IN (...) 限制掃描範圍,提升效率
- 多欄位同理:
SET col1 = CASE ..., col2 = CASE ...
PHP 動態構建示例
$display_order = [1=>4, 2=>1, 3=>2, 4=>3, 5=>9, 6=>5, 7=>8, 8=>9];
$ids = implode(',', array_keys($display_order));
$sql = "UPDATE categories SET display_order = CASE id ";
foreach ($display_order as $id => $ordinal) {
$sql .= sprintf("WHEN %d THEN %d ", $id, $ordinal);
}
$sql .= "END WHERE id IN ($ids)";
三大批量更新方案效能實測(10 萬條)
| 方案 | Real Time | User | Sys |
| 循環 UPDATE | 15.56s | 1.68s | 1.37s |
| 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 |
方案選擇指南
| 方案 | 適用場景 | 注意事項 |
| CASE WHEN | 更新既有行、欄位少、邏輯簡單 | 需主鍵/唯一鍵精確匹配 |
| REPLACE INTO | 全行覆蓋、可接受自增 ID 變化 | 缺失欄位重置為預設值 |
| INSERT ... ON DUPLICATE KEY UPDATE | 部分欄位更新、保留其它欄位 | 最通用、推薦 |
| 臨時表 JOIN | 超大批次(>10萬)、需事務一致性 | 需 CREATE TEMPORARY TABLE 權限 |
REPLACE vs ON DUPLICATE KEY 核心差異
REPLACE = DELETE + INSERT(全行重寫,自增 ID 變)
ON DUPLICATE KEY UPDATE = 僅更新指定欄位(自增 ID 不變)