必须加WHERE条件,否则触发全表扫描;漏写会导致数据库遍历每一行,即使仅几条匹配也需扫描全表,10万行可能卡几秒并锁大量无关行。

WHERE条件必须加,否则全表扫描风险极高
漏写 WHERE 是最常见也最危险的错误。没有它,CASE WHEN 会在全表每一行上求值,即使只有几条匹配,数据库仍要扫描全部数据——10 万行表可能卡住几秒,还可能锁住大量无关行。
正确做法是用 WHERE id IN (101, 102, 105) 显式限定范围,且确保 id 字段有索引。模糊匹配(如 name LIKE '%admin%')或函数包裹(如 DATE(created_at))会让这个索引失效,务必提前确认执行计划是否走了索引。
- 不要写成
UPDATE orders SET status = CASE ... END(无 WHERE) - 避免
WHERE status = 'pending' AND paid_at IS NOT NULL这类组合条件——除非两个字段都有联合索引,否则大概率退化为全表扫描 -
IN列表超过 1000 个值时,MySQL 可能报max_allowed_packet错误,应主动分批
每个字段必须独立写CASE,且ELSE不能省
CASE 表达式必须放在 SET 子句里,每个要更新的字段都得单独写一个 CASE,不能共用一个表达式去赋多个列。数据库是按行计算、逐字段赋值的,类型校验也各自独立。
漏掉 ELSE 会导致没匹配到任何 WHEN 的行,该字段被设为 NULL——这不是“保留原值”,而是静默清空。
- 正确:
SET name = CASE id WHEN 1 THEN 'Alice' ELSE name END, status = CASE id WHEN 1 THEN 'active' ELSE status END - 错误:
SET (name, status) = CASE id WHEN 1 THEN ('Alice', 'active') ...(多数数据库不支持元组赋值) - 错误:
ELSE NULL或直接不写ELSE——除非你明确想把未匹配行置空 - 所有
THEN分支返回值类型必须一致;混用1和'1'在 PostgreSQL 会报错,在 MySQL 可能隐式转但结果不可控
主键或唯一索引字段更新要绕开中间冲突
直接用 CASE 交换两行主键值(比如 id=1 → 2,id=2 → 1)几乎必然失败,报错类似 Duplicate entry '2' for key 'PRIMARY'。数据库不是先算完所有新值再统一写入,而是逐行检查约束。
安全做法是引入临时值占位,分两步更新:
- 第一步:
UPDATE t SET id = CASE id WHEN 1 THEN -2 WHEN 2 THEN -1 ELSE id END WHERE id IN (1,2) - 第二步:
UPDATE t SET id = CASE id WHEN -2 THEN 2 WHEN -1 THEN 1 END WHERE id IN (-1,-2) - 如果字段只是唯一索引(非主键),同样适用该逻辑
- MySQL 8.0+ 支持
UPDATE ... ORDER BY控制顺序,但 PostgreSQL 不支持,跨库方案仍推荐临时值法
分支过多(>50)时性能明显下降
CASE WHEN 分支数不是越多越好。MySQL 解析变慢,PostgreSQL 编译耗时上升,SQL Server 在超百分支时可能直接报 query processor could not produce a query plan。
当映射关系来自外部(如 Excel 导入、配置表),或者分支逻辑有规律(按区间、前缀、正则),优先考虑 JOIN 方式而非硬编码枚举。
- 分支超 50 个,建议拆成多条语句,或改用临时表 +
UPDATE JOIN - 例如:
CASE WHEN name LIKE 'A%' THEN 'group_a'比罗列 20 个 A 开头的名字更健壮 - 应用层拼 SQL 时,用户输入绝不能直插进
WHEN或THEN,必须来自可信枚举或预处理 - 空值判断要写
col IS NULL,而不是col = NULL(两者在 MySQL 和 PostgreSQL 中都不成立)











