業務常需「根據關聯表字段值更新本表字段」,MySQL 支持 UPDATE ... JOIN 標準語法,比子查詢更高效、語義更清晰。本文詳解語法、索引優化、事務一致性與避坑指南。
-- 單表更新引用另一表列
UPDATE t1
INNER JOIN t2 ON t1.key_col = t2.key_col
SET t1.target_col = t2.src_col
[WHERE t1.filter_col = 'val'];
-- 場景:訂單表根據用戶表更新用戶真實姓名冗余字段
UPDATE orders o
INNER JOIN users u ON o.user_id = u.id
SET o.user_real_name = u.real_name
WHERE o.user_real_name IS NULL OR o.user_real_name != u.real_name;
UPDATE t1
INNER JOIN t2 ON t1.id = t2.t1_id
SET t1.col_a = t2.col_x,
t1.col_b = t2.col_y,
t1.updated_at = NOW();
-- 若 t2 無匹配行,t1.col 置為默認值/NULL
UPDATE t1
LEFT JOIN t2 ON t1.key_col = t2.key_col
SET t1.target_col = COALESCE(t2.src_col, 'DEFAULT_VAL');
t1.key_col、t2.key_col 至少一方有索引,最好雙方都有VARCHAR 關聯 INT 導致全表掃描type=ref/eq_ref、rows 小、Using where 合理START TRANSACTION;
UPDATE t1 JOIN t2 ON t1.id = t2.t1_id SET t1.col = t2.val WHERE t1.id = 100;
-- 同事務內其他操作
COMMIT;
t1 修改行 + t2 讀取行(SELECT ... LOCK IN SHARE MODE 隱式)| 坑點 | 現象 | 解決 |
|---|---|---|
子查詢寫法 SET col = (SELECT ...) | 子查詢返回多行報錯;無索引慢;無法多字段更新 | 改用 UPDATE ... JOIN |
| 關聯條件含 NULL | NULL = NULL 為 UNKNOWN,不匹配 | 用 COALESCE 或 IS NULL 額外條件 |
| 更新同表別名衝突 | UPDATE t1 JOIN t1 AS t2 ... 易混淆 | 用清晰別名、加註釋 |
| 觸發器遞歸 | AFTER UPDATE 觸發器再更新同表 → 死循環 | 觸發器內判斷 IF NEW.col <> OLD.col 或用標志位表 |
-- 1. 先查出需更新的主鍵 ID 分批
CREATE TEMPORARY TABLE tmp_ids (id INT PRIMARY KEY);
INSERT INTO tmp_ids SELECT t1.id FROM t1 JOIN t2 ON t1.k = t2.k WHERE t1.col != t2.val LIMIT 5000;
-- 2. 循環更新
WHILE (SELECT COUNT(*) FROM tmp_ids) > 0 DO
START TRANSACTION;
UPDATE t1 JOIN tmp_ids USING (id) JOIN t2 ON t1.k = t2.k SET t1.col = t2.val;
COMMIT;
TRUNCATE tmp_ids;
INSERT INTO tmp_ids SELECT t1.id FROM t1 JOIN t2 ON t1.k = t2.k WHERE t1.col != t2.val LIMIT 5000;
DO SLEEP(0.1);
END WHILE;
UPDATE ... JOIN 是 MySQL 多表更新的標準高效寫法。關鍵在於:關聯列建索引、WHERE 縮小範圍、分批提交事務、EXPLAIN 驗證執行計劃。避免子查詢、隱式轉換、觸發器遞歸等陷阱。