大量資料需更新不同值時,逐條 UPDATE 效能極差。本文對比三種批量更新方案,附實測數據助你選型。
-- 目標:id=1→val=3, id=2→val=4, id=3→val=5
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 IN 縮小掃描範圍,提升效率-- REPLACE INTO(本質:DELETE + INSERT,缺失欄位會重置為預設值)
REPLACE INTO `test_tbl` (`id`, `dr`) VALUES (1,'2'), (2,'3'), (3,'4');
-- INSERT ... ON DUPLICATE KEY UPDATE(僅更新指定欄位,保留其他)
INSERT INTO `test_tbl` (`id`, `dr`) VALUES (1,'2'), (2,'3'), (3,'4')
ON DUPLICATE KEY UPDATE `dr` = VALUES(`dr`);
PRIMARY KEY 或 UNIQUE KEYREPLACE 會觸發 DELETE 觸發器,INSERT ... ON DUPLICATE KEY 不會CREATE TEMPORARY TABLE `tmp_update` (
`id` INT PRIMARY KEY,
`dr` VARCHAR(50)
);
INSERT INTO `tmp_update` VALUES (1,'gone'), (2,'xx'), (100000,'yy');
UPDATE `test_tbl` t
JOIN `tmp_update` tmp ON t.`id` = tmp.`id`
SET t.`dr` = tmp.`dr`;
CREATE TEMPORARY TABLES 權限| 方案 | Real Time | User CPU | Sys CPU |
|---|---|---|---|
| 逐條 UPDATE | 15.56s | 1.68s | 1.37s |
| REPLACE INTO | 1.39s | 0.06s | 0.01s |
| INSERT ... ON DUPLICATE KEY | 1.47s | 0.05s | 0.01s |
| 臨時表 + JOIN | 0.64s | 0.06s | 0.00s |
| 資料量 | 推薦方案 | 理由 |
|---|---|---|
| < 1,000 筆 | CASE WHEN | 語法簡單、無額外物件、易維護 |
| 1,000 ~ 100,000 筆 | INSERT ... ON DUPLICATE KEY UPDATE | 不重置其他欄位、效能優異、標準 SQL |
| > 100,000 筆 | 臨時表 + JOIN | 批次寫入、鎖定時間最短、可控制交易大小 |
$display_order = [1=>4, 2=>1, 3=>2, 4=>3, 5=>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)";
根據資料量與業務限制選擇適合方案,能讓批量更新從「分鐘級」降為「秒級」。