or常致全表扫描,因优化器遇任一不可索引分支即弃整体索引;应优先用in替代同字段多值or,或用union all拆分跨字段or并确保各子查询独立走索引。

OR让执行计划放弃索引,触发全表扫描
数据库优化器在看到 WHERE a = 1 OR b = 2 这类条件时,往往无法为两个分支同时选择合适的索引。尤其当 a 和 b 不是同一复合索引的前导列时,优化器大概率会直接放弃走索引,改用全表扫描——哪怕其中一侧条件本可命中索引。
UPDATE 更敏感:它不仅要定位数据,还要加锁、写日志、维护事务一致性。全表扫描意味着锁住更多行、产生更大 WAL 日志、消耗更多 buffer cache,速度自然断崖式下降。
- 常见现象:
EXPLAIN显示type是ALL,key为空,rows接近表总行数 - 视图嵌套会放大问题:你提到的复杂视图
V1如果本身含 JOIN 或函数,NOT EXISTS子查询 + 外层OR会让优化器更难估算成本,更容易选错策略 - 括号能起效,是因为显式分组帮助优化器识别逻辑单元,有时能触发“or-expansion”优化(如 Oracle),但不是所有数据库都支持
UNION ALL 是最稳的替代方案
把 UPDATE ... WHERE cond1 OR cond2 拆成两个独立 UPDATE,用 UNION ALL 在应用层或存储过程里合并逻辑,是最通用、兼容性最强的解法。
关键点不是语法变短,而是让每个子句都能独立走索引:
- 每个
WHERE分支必须能单独利用现有索引(比如key1 = ? AND key2 >= ?走(key1, key2)索引) - 用
UNION ALL而非UNION,避免去重开销;如果业务上两分支结果天然不重叠,甚至可并行执行 - 注意:MySQL 8.0+ 支持对
UPDATE ... SELECT做UNION ALL,但老版本需拆成两条独立UPDATE语句
别忽略 OR 两侧字段的数据分布差异
即使都走了索引,OR 仍可能引发隐性性能陷阱。比如:
- 左侧条件匹配 10 行,右侧匹配 10 万行——优化器可能按“平均成本”选错驱动表,导致嵌套循环变慢
- 一个字段高选择性(如主键),另一个低选择性(如状态码),混合后统计信息失真,执行计划不稳定
-
OR中混入函数或表达式(如DATE(create_time) = '2026-06-15')会让整侧失效,哪怕另一侧有索引也救不回来
这时候强行加索引没用,得先重构条件:用范围代替函数,或把计算移到应用层预处理。
视图复杂度会让 OR 问题雪上加霜
你提到的 NOT EXISTS 套复杂视图 V1,本身就是个风险点。视图展开后,OR 可能被下推到视图内部 JOIN 条件里,而视图定义中若含聚合、窗口函数或多次嵌套,优化器基本放弃精确估算,直接退化为保守策略——全表扫描 + 嵌套循环。
验证方法很简单:
- 把
V1替换成它的实际定义(SELECT ... FROM ...),再加OR测试,看是否依然慢 - 对视图涉及的基表,检查
key1/key2/key3是否有联合索引覆盖全部 JOIN 和过滤字段 - 某些数据库(如 SQL Server)允许在视图上建唯一聚集索引,但前提是视图满足严格条件(确定性、无聚合等),多数复杂视图不适用
真正棘手的从来不是 OR 本身,而是它暴露了底层索引缺失、统计信息陈旧、或视图设计与查询模式不匹配——这些才是拖慢 UPDATE 的根因。











