为什么--或者说什么时候--MySQL不为OR条件使用索引,如果它为AND条件使用的话?

问题描述 投票:1回答:1

我有一个表 the_table 带属性 the_table.id, the_table.firstValthe_table.secondVal 主键是 the_table.id当然是)。)

像这样在第一个非键属性上定义一个索引后。

CREATE INDEX idx_firstval  
ON the_table (firstVal);

这个 EXPLAIN 的结果是,下面的二连词(OR) 查询

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 没有使用。现在 EXPLAIN 以下共轭式的结果(AND) 查询

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选择在不连词查询中不使用索引,但在连词查询中却使用索引?

我搜了搜SO,按照在的回答建议,在 这条,"使用 OR 的查询中,通常会导致查询优化器放弃使用索引查找,并恢复到扫描"。然而,这并没有回答 何以 它发生了,只是它 是否.

另一条线 试图回答为什么不连贯查询不使用索引,但我认为它在这方面是失败的--仅仅是断定上位机使用的是小型数据库。我想知道的是 差异 和连词的情况。

mysql sql indexing query-optimization explain
1个回答
2
投票

因为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)


1
投票

我很惊讶MySQL对这两个查询中的任何一个都使用了索引。 这里正确的索引应该是一个复合索引,它涵盖了在 WHERE 子句。

CREATE INDEX idx ON the_table (firstVal, secondVal);

至于为什么MySQL会在第二种情况下使用索引,一种可能是,如果大部分的记录在 the_tablefirstVal 价值,这些价值是 A. 在这种情况下,只要知道平等 the_table.firstVal = 'A' 是假的,这意味着,整个结果中的 WHERE 子句会被知道(为假)。 所以,为什么要使用索引的答案可能与下面的 基数 的准确数据。 但在任何情况下,都可以考虑使用综合指数来覆盖所有的基础。

© www.soinside.com 2019 - 2024. All rights reserved.