當需要根據不同主鍵更新不同值時,循環執行多條 UPDATE 會產生大量網絡往返與解析開銷。MySQL 提供兩種單語句批量更新方案,大幅降低交互次數,提升吞吐。
利用 CASE 表達式在單條 UPDATE 中根據主鍵賦予不同值。
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 動態構造示例:
$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)";
// 執行 $sql
優點:標準 SQL,跨庫兼容;注意:超長 SQL 可能受 max_allowed_packet 限制,極大批次建議分批。
利用主鍵/唯一鍵衝突觸發更新,適合「不存在則插入,存在則更新」的業務(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) 引用插入行中對應列的值,避免重複書寫。
優點:語句緊湊,天然支持批量 Upsert;缺點:非標準 SQL,遷移其他庫需改寫;若表有自增主鍵且插入行指定了 id,需確保無主鍵衝突導致非預期更新。
| 維度 | CASE WHEN | ON DUPLICATE KEY UPDATE |
|---|---|---|
| SQL 標準性 | 標準 | MySQL 專有 |
| 適用場景 | 純更新、多字段不同值 | Upsert、主鍵衝突即更新 |
| 批量大小限制 | 受 max_allowed_packet 限制 | 同樣受限,但語句更緊湊 |
| 索引要求 | 無額外要求 | 需主鍵或唯一索引 |
| 觸發器行為 | 觸發 UPDATE 觸發器 | 觸發 INSERT/UPDATE 觸發器(視衝突) |
CASE WHEN,通用性強。ON DUPLICATE KEY UPDATE,配合分批提交(如每 500 行)。SET autocommit=0)並顯式事務提交,進一步減少日志刷盤開銷。max_allowed_packet,必要時調大(如 64M)。單條 SQL 批量更新是減少數據庫交互、提升寫入吞吐的關鍵手段。根據業務是「純更新」還是「Upsert」,選擇 CASE WHEN 或 ON DUPLICATE KEY UPDATE,並注意分批與參數調優,即可獲得數量級性能提升。