mysql中or条件部分字段无索引时优化器通常放弃走索引,因全表扫描成本更低;即使有索引字段也常失效,推荐用union替代以稳定命中索引。

OR条件中部分字段无索引,优化器放弃走索引
MySQL在遇到 OR 时,并不会为每个分支单独走索引再合并结果。它会评估:如果只对有索引的字段走索引,剩下没索引的字段仍需全表扫描,那不如直接全表扫一遍更省事——尤其是当无索引字段的筛选基数较大时。
常见错误现象:EXPLAIN 显示 type=ALL、key=NULL,哪怕其中某个条件字段明明建了索引。
- 示例:
SELECT * FROM user WHERE name = 'Alice' OR age = 25;—— 若age列未建索引,name的索引大概率失效 - 即使
name和age都有单列索引,优化器也可能不选任一索引,因为 MySQL 5.7+ 默认不支持对多个单列索引做“索引合并”(index_merge),除非显式启用或满足严格条件 - 真正生效的场景极少:仅当所有
OR分支字段都有索引,且优化器判定index_merge成本更低时才可能启用(可通过EXPLAIN中的type=index_merge确认)
用 UNION 替代 OR 才能稳定命中索引
UNION 把逻辑拆成独立子查询,每个子句可单独走索引,再去重合并结果。这是最可控、兼容性最好的解法。
- 写法:
SELECT * FROM user WHERE name = 'Alice' UNION SELECT * FROM user WHERE age = 25; - 注意:
UNION自动去重,如确定无重复或不需要去重,改用UNION ALL性能更高 - 缺点:无法使用
ORDER BY/LIMIT跨整个结果集(需外包一层查询),且不能引用外层别名
联合索引 + OR 不等于“天然兼容”
有人误以为给 (name, age) 建联合索引就能让 name = ? OR age = ? 走索引——实际不行。联合索引只支持最左前缀匹配,OR 是并列条件,不是顺序过滤。
-
WHERE name = 'Alice' AND age = 25可走(name, age)索引 -
WHERE name = 'Alice' OR age = 25仍无法利用该联合索引,因为OR没有“最左”可依 - 若真要覆盖这类查询,更现实的做法是分别建
INDEX(name)和INDEX(age),再依赖优化器是否启用index_merge(但不可强依赖)
隐式类型转换会让 OR 雪上加霜
当 OR 任一分支触发隐式转换(比如 user_id = 123,而字段是 VARCHAR),该分支必然不走索引;此时另一个分支即使类型正确,整个 OR 查询也大概率被优化器判为“不可靠”,直接弃用索引。
- 典型错误:
WHERE id = '1001' OR status = 1—— 若id是字符串类型,'1001'不加引号就会触发转换,索引失效 - 验证方法:逐个执行分支,用
EXPLAIN看各自是否走索引;只要有一个不走,OR整体就危险 - 修复原则:所有字段值必须与定义类型严格一致,字符串加引号,数字不加引号
真正难处理的不是语法本身,而是 OR 让优化器失去确定性——它不再能预估每个分支的筛选率,也无法复用索引的有序性。线上关键查询,优先考虑重构为 UNION 或拆成应用层多次查询。











