我有个SQL查询用了多个OR条件,比如WHERE status='A' OR status='B' OR status='C',发现走不了索引,改成IN也一样,这种情况应该怎么优化?

新手上路,请多包涵

我有个SQL查询用了多个OR条件,比如WHERE status='A' OR status='B' OR status='C',发现走不了索引,改成IN也一样,这种情况应该怎么优化?

我有个SQL查询用了多个OR条件,比如WHERE status='A' OR status='B' OR status='C',发现走不了索引,改成IN也一样,这种情况应该怎么优化?

阅读 887
6 个回答

状态加索引意义不大。状态值只有几个,放到海量数据,重复很多,区分度并不大。

image.png
这样是走了,但是你的数据量有这么大嘛,强制走索引提升大?

首先,状态字段加索引是有意义的。如果不加索引,频繁带过滤条件的查询会给数据库带来很大压力。

其次,我认为你目前无法通过状态条件走索引的根本原因在于状态值的设计

不知道你使用的是哪种数据库。通用的做法是使用位标志(bit flag),即把多个状态放在一个整数字段里。使用位标志在各种数据库中都受支持,因为最终在数据库里存的只是一个 int 值,而 int 天生非常适合建立索引。

如果使用支持原生枚举的数据库,例如 PostgreSQL、SQL 数据库等,可以创建相应的枚举记录(如 ENUM )。这样,搜索条件可以直接使用这些状态枚举,实际在存储层面仍会转化为对应的 int 值,索引也可以很方便地建立。

WHERE status='A' OR status='B' OR status='C'
等价于
WHERE status IN ('A','B','C')
这两种写法本身都能走索引,不走索引可能是status 字段没有建索引、IN 的值太多 / 数据分布不均,优化器认为全表更快、统计信息过时,MySQL 误判

先explain一下sql语句,看看优化器怎么说,也许放弃了使用索引,主动选择全表扫描?哪个索引可以走但是实际并没有走?

如果 status 列只有几个值(比如 A/B/C/D),那么:
命中比例可能是 60%~90%
优化器判断:走索引 + 回表 ≈ 比全表扫描更慢
就直接选 全表扫描

方案1:改写成 UNION ALL

举例
SELECT id, name FROM table WHERE status = 'A'
UNION ALL
SELECT id, name FROM table WHERE status = 'B'
UNION ALL
SELECT id, name FROM table WHERE status = 'C';

优点:
每个子查询都可以 单独走索引
避免优化器放弃索引
注意:
如果有重复,需要用 UNION替换UNION ALL,缺点就会走会排序 / 去重(Using temporary + filesort),相当于使用临时表,排序或哈希,无法“流式返回”,性能明显比 UNION ALL 差

去重写法
  • DISTINCT写法

    SELECT DISTINCT id, name
    FROM (
      SELECT id, name FROM table WHERE status = 'A'
      UNION ALL
      SELECT id, name FROM table WHERE status = 'B'
      UNION ALL
      SELECT id, name FROM table WHERE status = 'C'
    ) t;
    
  • GROUP BY 写法

    SELECT id, name
    FROM (
      SELECT id, name FROM table WHERE status = 'A'
      UNION ALL
      SELECT id, name FROM table WHERE status = 'B'
      UNION ALL
      SELECT id, name FROM table WHERE status = 'C'
    ) t
    GROUP BY id, name;
    

方案2:建立覆盖索引(强烈推荐)

如果你是:

SELECT id, name FROM table WHERE status IN ('A','B','C')

建覆盖索引:

INDEX(status, id, name)

效果:
变成 覆盖索引扫描
不回表 → 成本大降

宣传栏