首页/数据/SQL 速查/OR 条件与索引利用
性能与执行计划72 条

OR 条件与索引利用

同列多个等值条件优先写成 IN;跨列 OR 往往触发索引合并或全表扫描,改写 UNION 更可控。

SQL 速查数据更新于 2026-10-13

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 时先确认条件本身的写法是否可用索引。

易错点

  • 跨列 OR 只加索引不验证:是否走索引合并以 EXPLAIN 为准,不少场景仍是全表扫描
  • UNION 改写后忽略去重语义:原 OR 会去重,改写时按业务选择 UNION 或 UNION ALL
  • 把 OR 链写成几十个条件:同列时改用 IN 更短也更易优化,跨列时可考虑拆成多个分支
  • 一侧条件对列做了函数运算:该分支用不上索引,整体退化成全扫描
  • 以为索引合并一定比全表扫描快:行数多时求并集开销更大,要用实测执行时间与读行数判断

数据来源与参考

  • MySQL 8.0 Reference Manual §8.2.1.3 Index Merge Optimization
  • PostgreSQL Documentation · Multicolumn Indexes
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌