直接结论:别硬扛or,优先用in替同字段多值,用union all拆跨字段条件,并确保每个分支都能走索引——否则改写只是把一次全表扫描变成多次。

直接结论:别硬扛 OR,优先用 IN 替同字段多值,用 UNION ALL 拆跨字段条件,并确保每个分支都能走索引——否则改写只是把一次全表扫描变成多次。
同一字段的多个等值 OR 必须改 IN
比如 status = 'draft' OR status = 'pending' OR status = 'review',这种写法不仅啰嗦,还容易触发隐式类型转换或让优化器放弃索引合并。改成 status IN ('draft', 'pending', 'review') 后,MySQL 能明确走 range 访问类型,且执行计划稳定。
-
IN值数量建议控制在 500 以内;超了可能触发临时表,反而变慢 -
IN (SELECT ...)不推荐,尤其子查询返回 NULL 或结果集大时,常被重写为DEPENDENT SUBQUERY,性能崩盘 -
col IN (1, NULL)不会匹配NULL行,而col = 1 OR col IS NULL可以——行为不一致,不能无脑替换
不同字段的 OR 必须拆成 UNION ALL
像 user_id = 123 OR order_no = 'NO2026001' OR created_at > '2026-06-01' 这种跨字段组合,MySQL 几乎必然退化为 type: ALL 扫描。这时唯一可控的解法是手动拆分,让每个子查询独立走自己的索引。
- 每个子查询必须单独
EXPLAIN验证:type是ref或range,key显示命中索引,rows显著小于总行数 - 务必用
UNION ALL,不是UNION;去重开销在万级数据上就能明显拖慢 - 字段顺序、类型、可空性必须完全一致,否则报错
ERROR 1222 - 原始查询带
LIMIT或ORDER BY?不能只在外层加——得先在各子查询里加LIMIT N,再外层二次排序,否则漏数据
为什么加索引有时也救不了 OR 查询
很多人建了单列索引仍慢,问题不在 OR 写法,而在索引本身没对上路。MySQL 的 index_merge 触发条件极苛刻:所有 OR 字段必须各自有独立单列索引,且不能含函数、不能有前导通配符(如 LIKE '%x')、不能隐式类型转换。
-
UPPER(name) = 'ABC'或DATE(created_at) = '2026-07-01'会让对应字段索引完全失效 - 复合索引中非前导列(如
(a, b)上查b = 1)无法被 OR 分支利用 -
EXPLAIN中出现Using temporary; Using filesort,基本说明 GROUP BY 已被迫落盘,此时光调 OR 没用,得重写逻辑或预聚合
复杂 OR 场景下容易被忽略的点
当 OR 条件混入范围查询(>、BETWEEN)、存在性判断(IS NULL)或全文模糊(LIKE '%x%'),UNION ALL 维护成本飙升,强行拆分反而更慢。这时候该考虑的是数据层重构,而不是 SQL 层微调。
- 若分支超过 5 个,先问一句:这真是实时查询需求?还是报表类场景?后者更适合物化视图或定时任务预统计
- 混合
created_at > '2026-01-01' OR remark LIKE '%urgent%'这种,remark字段该加全文索引,而不是硬塞进 OR - 涉及
OR+AND混用时(如(a=1 OR b=2) AND c=3),括号必须显式写出,ORM 框架生成的链式where/orWhere极易漏括号,导致逻辑错乱











