8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

为什么(或者何时) MySQL 不对 OR 条件使用索引,而对 AND 条件使用索引?

KYL3R 2月前

60 0

我有一个表 the_table,其属性为 the_table.id、the_table.firstVal 和 the_table.secondVal(当然,主键是 the_table.id)。在第一个非键属性上定义索引后……

带有属性 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 在查询中使用通常会导致查询优化器放弃使用索引查找并恢复到扫描”。然而,这并没有回答 why 会发生这种情况,只是它确实 发生了 .

另一个帖子 试图回答为什么分离查询不使用索引,但我认为它没有做到这一点 - 它只是得出结论,原帖者使用的是小型数据库。我想知道 分离和连接的情况 之间的区别

帖子版权声明 1、本帖标题:为什么(或者何时) MySQL 不对 OR 条件使用索引,而对 AND 条件使用索引?
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由KYL3R在本站《mysql》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 因为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)

  • 关于“如果存在包含 firstVal 和 secondVal 的复合索引 (...)”:我按照 @TimBiegeleisen 的建议检查了复合索引,OR 仍然会进行全表扫描,而不使用索引。这对我来说真的很奇怪;我认为优化器希望在两种情况下都使用此类索引,但对于分离查询则不需要。

  • 对于简单集,全表扫描的成本很低,因此优化器可能更倾向于使用全表扫描的访问计划,而不是使用索引。如果使用索引的计划将访问索引中的页面和数据表中的页面……对于一个非常小的表,忽略索引并仅从表中获取页面的计划的成本估计较低,这并不奇怪。

  • 如果 SELECT 列表中有 *,MySQL 将需要访问底层表中的数据页,以查找索引中不可用的任何列。如果查询的覆盖索引可用,我们更有可能看到索引被使用。它不会是范围扫描操作,因为它需要检查每一行,即完整扫描,但它可以在索引而不是表上进行扫描。尝试放弃 SELECT 列表中的 *,并让查询仅引用索引中可用的列,例如 SELECT t.firstVal, t.secondVal FROM the_table t WHERE ... OR ...

  • @spencer7593 我不认为这是完全正确的(一个表一个索引)。在我的例子中,我有一张非常大的表,我在其中执行 SELECT t.id, t.values FROM table t WHERE id IN (subquery1) OR id IN (subquery2) - 这是一个单一索引(在 id 上),并且足够大,索引可以带来巨大的改进,但 MySQL 仍然不使用它。

  • 我很惊讶 MySQL 对这两个查询都使用了索引。这里要使用的正确索引是复合索引,它涵盖子句中的两列 WHERE

    CREATE INDEX idx ON the_table (firstVal, secondVal);
    

    至于为什么 MySQL 在第二种情况下使用索引,一种可能性可能是如果中的大多数记录的 the_table firstVal 不是 。在这种情况下,仅仅知道相等性 A 为假就意味着该子句的整个结果 the_table.firstVal = 'A' WHERE 基数 有关 。但无论如何,请考虑使用复合索引来覆盖所有基础。

  • 您说得没错,“A”是该表中的罕见值(124 个条目),因此我进行了一些额外的查询以获取上下文。两个最常见的值(分别为 1747 个和 1446 个条目,称为“C”和“D”)都具有完全相同的 EXPLAIN。按照您的建议定义复合索引,AND 会切换到它,但 OR 仍然会进行全表扫描。

  • @Mew 我不知道您的实际数据或有多少条记录,但有时如果记录太少,SQL 数据库甚至不会使用索引。

  • kos 2月前 0 只看Ta
    引用 10

    @Mew:如果复合索引是查询的覆盖索引,我们可能会看到 OR 查询的完整索引扫描,如果查询中引用的所有列都包含在索引中。例如,在 SELECT 列表中放弃 * 并改为执行 SELECT firstVal, secondVal FROM the_table ...` 的查询(优化器不能使用范围扫描操作,它仍然需要检查每一行,但如果它可以完全从索引满足查询而不需要在底层表中查找页面,我们称之为查询的覆盖索引)。

  • R71 2月前 0 只看Ta
    引用 11

    @TimBiegeleisen:啊,也许我应该具体说明一下:the_table 中有 3436 行,正如我的帖子中第一个输出所显示的那样(在“rows”列中)。

  • 这是一张很小的表。我猜索引在 OR 情况下不会有太大帮助,所以 MySQL 只能回退到表扫描。

返回
作者最近主题: