MySQL简单联表用OR触发全表扫描,如何避免并实现范围索引扫描?
问题根因
跨表的OR条件无法被MySQL优化器直接做索引优化:当OR两边的筛选条件分别属于关联的两张表时,优化器无法确定先扫描哪张表能过滤足够多的数据,因此会选择全表扫描后再做关联和条件判断,这就是你把OR改成AND后能走索引、OR就走全表的核心原因。
可行优化方案
最优的改写方式是将OR条件拆分为两个独立查询的结果并集,用UNION(自动去重,完全符合原OR的语义)拼接,拆分后的两个查询各自可以命中对应表的position索引:
-- 匹配table1.position在指定范围的关联数据 SELECT * FROM table1 INNER JOIN table_2 ON table1.id = table_2.table1_id WHERE table1.position BETWEEN 5000 AND 5500 UNION -- 匹配table_2.position在指定范围的关联数据 SELECT * FROM table1 INNER JOIN table_2 ON table1.id = table_2.table1_id WHERE table_2.position BETWEEN 5000 AND 5500
额外索引优化建议
- 针对table_2,建议将现有单值索引替换为联合索引
idx_table2_pos_t1id(position, table1_id),第二个查询筛选table_2.position范围后可以直接从索引中取到关联需要的table1_id,无需回表查主键数据,进一步提升效率 - 如果你不需要查询所有字段,建议把
SELECT *替换为具体需要的字段,还可以针对性构建覆盖索引,完全避免回表操作 - 如果业务场景可以确认两个子查询的结果没有重叠(即不会出现同时满足两张表position都在范围的行),可以把
UNION改成UNION ALL,省去去重的开销,执行速度更快
内容的提问来源于stack exchange,提问作者jossefaz
相关产品推荐
相关产品推荐

