高性能批量更新应避免循环体,优先用单SQL实现:CASE WHEN(≤1000行)、INSERT...ON DUPLICATE KEY UPDATE(需主键/唯一索引)、临时表JOIN(≥5000行),三者均优于存储过程CURSOR+LOOP。

UPDATE 本身不支持循环体;MySQL 存储过程里的 CURSOR + LOOP 是真循环,但性能极差,不应作为批量更新的方案。它本质是单行执行、逐条发语句、锁行时间长、网络/解析开销大,1000 行就可能比 CASE WHEN 慢几十倍。
真正高性能的“批量更新”,靠的是一条 SQL 完成多行操作,不是靠写循环体。
CASE WHEN 是最常用且安全的批量更新写法
适用于:更新行数 ≤ 1000、主键或唯一键明确、每行新值不同、MySQL 5.0+。
-
CASE WHEN必须配合WHERE id IN (...),否则漏掉的行会被设为NULL(除非加ELSE) - 所有
THEN分支返回值类型必须一致,混用字符串和数字会触发隐式转换甚至报错 -
id字段必须有索引,否则WHERE id IN会全表扫描并锁表 - 示例:
UPDATE users SET status = CASE id WHEN 1 THEN 'active' WHEN 2 THEN 'inactive' WHEN 5 THEN 'pending' ELSE status END WHERE id IN (1, 2, 5);
INSERT ... ON DUPLICATE KEY UPDATE 效率最高但有约束
适用于:目标表有主键或唯一索引、你控制输入数据、允许“插入不存在的记录”(或确保只更新不插入)。
- 不是纯更新语句,而是“插入优先,冲突则更新”,所以必须保证
INSERT的字段覆盖所有唯一键 -
VALUES(col)引用的是当前这一行的 INSERT 值,不是原表值,写错容易丢数据 - 示例:
INSERT INTO users (id, name, status) VALUES (1,'A','active'), (2,'B','inactive'), (5,'C','pending') ON DUPLICATE KEY UPDATE name = VALUES(name), status = VALUES(status);
- 注意:如果
id=3不存在,这条语句会插入新记录——这不是“更新”,得提前确认业务是否允许。
临时表 JOIN 更新适合万级+数据,但要注意权限和索引
适用于:要更新的数据来自外部(如 CSV 导入、子查询结果)、行数 ≥ 5000、需要复用中间逻辑。
- 必须对临时表的关联字段建索引(哪怕只是
CREATE TEMPORARY TABLE ... (id INT PRIMARY KEY)) - 不能直接
JOIN子查询(会报You can't specify target table for update in FROM clause),必须包装成派生表并加别名 - 临时表需
CREATE TEMPORARY TABLE权限;会话断开自动销毁,但没显式DROP时仍占内存 - 示例:
CREATE TEMPORARY TABLE tmp_updates (id INT PRIMARY KEY, new_status VARCHAR(20)); INSERT INTO tmp_updates VALUES (1,'active'),(2,'inactive'),(5,'pending'); UPDATE users u JOIN tmp_updates t ON u.id = t.id SET u.status = t.new_status;
真正容易被忽略的点是:没有所谓“高性能的循环体”,只有“避免循环”的高性能写法。存储过程里的 WHILE 或 REPEAT 循环在批量更新场景下就是反模式——它把本可一次完成的事拆成 N 次,还放大了锁竞争和事务日志压力。实际压测中,10 万行用 CASE WHEN 卡死,而 ON DUPLICATE KEY 仍稳定,原因就在这里。



















