OR 能不能用上索引
关键看 OR 跨不跨列。同一列上的多个等值条件,优化器通常合并成 IN 或范围扫描,走一个索引;跨列的 OR 可能走索引合并,也可能直接全表扫描,开销不稳定。把跨列 OR 改写成 UNION,让每个分支各自选择合适的索引,通常更可控。
基本信息
| 同列多个 OR | 常被合并为 IN 或 range,能用索引 |
|---|---|
| 跨列 OR | 可能走索引合并,也可能全表扫描 |
| UNION 改写 | 每个分支独立选择索引,更可控 |
| 去重语义 | 原 OR 天然去重,改写时按需选 UNION 或 UNION ALL |
| 验证方式 | 以 EXPLAIN 输出为准,不要凭语法下判断 |
典型使用场景
- 按多个用户 ID 查询:用 IN 而不是 OR 链
- 跨列条件合并查询遇到全表扫描,需要改写
- 把慢的 OR 查询拆成分支查询逐段优化
- 排查 EXPLAIN 里出现的索引合并节点
- 报表条件拼接后走不动索引的优化
示例
示例
-- 同列:IN 等价于多个 OR,通常走范围扫描
SELECT * FROM orders WHERE user_id IN (100, 101, 102);
-- 跨列 OR:可能走索引合并,也可能全表扫描
SELECT * FROM orders WHERE user_id = 100 OR status = 'paid';
-- 改写为 UNION:两个分支各自选择索引
SELECT * FROM orders WHERE user_id = 100
UNION
SELECT * FROM orders WHERE status = 'paid';
-- 确认业务不需要去重时用 UNION ALL 省掉去重
SELECT id FROM orders WHERE user_id = 100
UNION ALL
SELECT id FROM orders WHERE status = 'paid';三种写法对照
| 写法 | 可能的执行方式 | 注意 |
|---|---|---|
| 同列 OR | 合并为 IN 或范围扫描 | 条件多时控制列表长度 |
| 跨列 OR | 索引合并或全表扫描 | 是否走索引用 EXPLAIN 确认 |
| UNION | 两个分支各自选索引 | 默认去重,有额外成本 |
| UNION ALL | 两分支直接拼接 | 确认业务确实不需要去重 |
索引合并不是免费的:它要把多个索引的结果求并集或交集,代价可能高于单索引扫描,行数一多优化器就会放弃并改走全表扫描。
对列做函数运算或类型转换会让 OR 两侧都用不上索引,例如把字符串列与数字比较。排查跨列 OR 时先确认条件本身的写法是否可用索引。