mysql中or查询慢的本质是索引失效,因优化器常无法对含or的多条件有效利用复合索引,尤其当涉及不同字段、函数或类型转换时易退化为全表扫描;可用union all替代、in优化同字段多值、避免嵌套or+and混合逻辑。

MySQL 用 OR 查询慢,本质是索引失效
MySQL 在多数情况下无法对含 OR 的多条件查询有效利用复合索引,尤其当 OR 两边涉及不同字段或存在函数/类型转换时,优化器常退化为全表扫描。不是语法写错了,是执行计划本身就不打算走索引。
常见错误现象:EXPLAIN 显示 type=ALL 或 key=NULL,哪怕单个条件单独执行都很快;OR 连接的字段没被同时覆盖在同一个索引里;一边用了索引,另一边没索引,整个条件就放弃走索引。
- 只有当
OR所有分支都命中同一索引的最左前缀,且无隐式转换、无函数包裹时,才可能走索引 - 如果
OR涉及NULL判断(如col IS NULL OR col = ?),即使有索引也大概率失效 -
OR和LIKE '%xxx'搭配基本等于放弃索引
用 UNION ALL 替代 OR 的实操要点
把一个带 OR 的查询拆成多个独立子查询,各自走自己的索引,再用 UNION ALL 合并结果——这是最常用也最可控的替代方案。注意必须是 UNION ALL,不是 UNION,否则去重开销反而更大。
使用场景:两个(或少数几个)独立条件,各自能走不同索引,或同一字段的不同取值范围(如 status IN (1,3) 可改写为 status = 1 UNION ALL status = 3)。
示例对比:
SELECT id, name FROM users WHERE status = 1 OR city = 'Shanghai';
→ 改写为:
SELECT id, name FROM users WHERE status = 1 UNION ALL SELECT id, name FROM users WHERE city = 'Shanghai' AND status != 1;
- 第二条
WHERE加AND status != 1是为了去重(避免重复记录),但仅在业务允许精确控制时加;若主键/唯一键已保证无交集,可省略 - 每个子查询必须有明确的索引支撑:
status有索引,city有索引,否则拆了也没用 -
UNION ALL不检查重复,所以结果顺序不保证,需要统一排序时必须在外层加ORDER BY
IN 能代替部分 OR,但别滥用
当 OR 是同一字段的多个等值判断(如 id = 1 OR id = 2 OR id = 3),直接换成 IN (1,2,3) 即可,MySQL 对 IN 的优化足够好,且仍能走索引。
但要注意边界:
-
IN的值数量不宜超过几百个,否则解析和执行开销上升,某些版本会触发临时表或文件排序 - 如果
IN里混入子查询(如IN (SELECT ...)),性能可能比OR更差,优先考虑JOIN -
IN对NULL处理特殊:col IN (1, NULL)永远不匹配NULL行,和OR行为不一致
真正该警惕的是嵌套 OR + AND 的混合逻辑
比如 WHERE (a = 1 AND b = 2) OR (c = 3 AND d = 4),这种结构 MySQL 几乎不可能走索引,UNION ALL 是唯一靠谱解法,但得确保每个括号内条件能各自命中索引。
容易踩的坑:
- 没验证每个子查询是否真走了索引——务必对每个
UNION分支单独EXPLAIN - 忽略数据倾斜:某个分支返回几百万行,另一个只返回几行,合并后网络传输和客户端处理压力集中在大结果集上
- 事务隔离级别影响:多个子查询在 RR 级别下可能看到不一致快照,尤其涉及更新中的表
复杂点从来不在语法怎么写,而在于你有没有看过每一条执行计划里 key 和 rows 到底填了什么。











