我有一个表 the_table
带属性 the_table.id
, the_table.firstVal
和 the_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的执行计划中,一张表只用一个索引。
如果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对这两个查询中的任何一个都使用了索引。 这里正确的索引应该是一个复合索引,它涵盖了在 WHERE
子句。
CREATE INDEX idx ON the_table (firstVal, secondVal);
至于为什么MySQL会在第二种情况下使用索引,一种可能是,如果大部分的记录在 the_table
有 firstVal
价值,这些价值是 不 A
. 在这种情况下,只要知道平等 the_table.firstVal = 'A'
是假的,这意味着,整个结果中的 WHERE
子句会被知道(为假)。 所以,为什么要使用索引的答案可能与下面的 基数 的准确数据。 但在任何情况下,都可以考虑使用综合指数来覆盖所有的基础。