我有一个表the_table
,其属性为the_table.id
、the_table.firstVal
和the_table.secondVal
(主键当然是 the_table.id
)。
在第一个非键属性上定义索引后,如下所示:
CREATE INDEX idx_firstval
ON the_table (firstVal);
以下析取 (OR
) 查询的 EXPLAIN
结果
SELECT * FROM the_table WHERE the_table.firstVal = 'A' OR the_table.secondVal = 'B';
是
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra
| 1 | SIMPLE | the_table | ALL | idx_firstval | NULL | NULL | NULL | 3436 | Using where
这表明索引idx_firstval
未被使用。现在,以下联合 (AND
) 查询的 EXPLAIN
结果
SELECT * FROM the_table WHERE the_table.firstVal = 'A' AND the_table.secondVal = 'B';
是
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra
| 1 | SIMPLE | the_table | ref | idx_firstval | idx_firstval | 767 | const | 124 | Using index condition; Using where
这显示了这次正在使用的索引。
为什么MySQL 选择不在析取查询中使用索引,而在连接查询中使用索引?
我已经搜索过了,正如this thread中的答案所建议的那样,“在查询中使用 OR
通常会导致查询优化器放弃使用索引查找并恢复到扫描”。然而,这并不能回答为什么会发生这种情况,而只是回答它确实发生了。
Another thread试图回答为什么析取查询不使用索引,但我认为它这样做失败了 - 它只是得出结论,OP正在使用一个小型数据库。我想知道析取和连接情况之间的区别。
最佳答案
因为MySQL执行计划只对一张表使用一个索引。
如果 MySQL 对 idx_firstval
使用范围扫描来满足 firstVal
列上的相等谓词,那么 MySQL 仍需要检查 secondVal
上的条件专栏。
使用AND
,MySQL只需要检查索引范围扫描返回的行。需要检查的行集受条件约束。
使用OR
,MySQL需要检查索引范围扫描未返回的行,以及表中的所有其余行。如果没有索引,则意味着对表进行全面扫描。如果我们对表进行完整扫描来检查 SecondVal,那么检查扫描中的两个条件(即包含索引访问和完整访问的计划)的成本会更低。扫描会更贵。)
(如果包含firstVal和secondVal的复合索引可用,那么对于OR
查询,可以想象优化器可能认为通过执行以下操作来检查表中的所有行的成本较低全索引扫描,然后查找数据页。)
当我们了解优化器可以执行哪些操作时,我们就可以避免使用 OR 并重写查询,以返回等效的结果集,并使用更明确定义组合的查询模式两套
SELECT a.*
FROM the_table a
WHERE a.firstVal = 'A'
UNION ALL
SELECT b.*
FROM the_table b
WHERE b.secondVal = 'B'
AND NOT ( b.firstVal <=> 'A' )
(如果我们希望按特定顺序返回行,请添加 ORDER BY)
关于mysql - 如果 MySQL 对 AND 条件使用索引,为什么或何时不对 OR 条件使用索引?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61596458/