为什么“加入或”慢两个“加入”?

时间:2013-06-28 07:20:40

标签: mysql

以下查询很快:

SELECT *
FROM   apple
       LEFT JOIN banana b1
              ON apple.id = b1.one
       LEFT JOIN banana b2
              ON apple.id = b2.two
WHERE  b1.id IS NULL
       AND b2.is IS NULL  

虽然以下内容很慢:

SELECT *
FROM   apple
       LEFT JOIN banana
              ON apple.id = banana.one
                  OR apple.id = banana.two
WHERE  banana.id IS NULL  

任何人都可以解释为什么用“或”做一个“join on”语句比加入两个表要慢得多?

3 个答案:

答案 0 :(得分:5)

在连接的第一个查询中,mysql将仅使用banana表中的一列进行查找(N个查找,其中N是来自apple表的记录的nb)。

在第二个查询中,它必须使用来自banana表的2列进行查找,在最坏的情况下,它必须进行NxN查找,其中N是来自{的记录的nb。 {1}}

您可以详细了解所使用的算法here

此外,您还可以查看 Stan McGeek提供的fiddle中的apple输出

更新:也请记住:

  

如果使用LEFT JOIN查找某些表中不存在的行   您有以下测试:在WHERE部分中col_name IS NULL,其中   col_name是一个声明为NOT NULL的列,MySQL停止   在它之后搜索更多行(对于特定的键组合)   找到了一行与LEFT JOIN条件相匹配的行。

答案 1 :(得分:2)

出现这种令人惊讶的情况是因为OR运算符组合了两列,因此阻止在任一列上使用任何索引。

我们假设banana有两个索引,一个在banana.one上,另一个在banana.two上。

在第一个查询中,优化器将能够为每个不同的JOIN使用索引,因为它们在两个不同的传递中执行。每个JOIN将使用banana上的两个索引之一(复杂度= Nx2 = N,其中N =苹果数)。

在第二个版本中,只有一个JOIN和一个单一传递。但是JOIN只能使用一个索引。由于两个索引都不足(两个JOIN条件中只有一个被覆盖),它不会使用任何索引并进行表banana的完整扫描(复杂度= NxMx2 = NxM,其中M =香蕉数量。)

您可以使用每个查询的EXPLAIN SELECT ...进行检查。

请注意,(banana.one, banana.two)上的两列索引同样没用。

答案 2 :(得分:1)

您的原始查询

SELECT *
FROM      apple
LEFT JOIN banana b1 ON apple.id = b1.one
LEFT JOIN banana b2 ON apple.id = b2.two
WHERE  b1.id IS NULL
   AND b2.is IS NULL

似乎正在查找列applesbanana上没有匹配banana.one的所有banana.two。如果是这样的话,你为什么不做那些显而易见的事情,只是简单地陈述问题:

select *
from apple a
where not exists ( select *
                   from banana b
                   where b.one = a.id
                 )
  and not exists ( select *
                   from banana b
                   where b.two = a.id
                 )

假设apple的主键idbanana的外键列one上有索引,任何体面的查询优化器都应该可以做到这一点。和two

如果您在香蕉列onetwo上有覆盖索引,例如

create index banana_one_two on banana ( one , two )

你表现不佳的查询也很慢。

检查您获得的执行计划可能会为您提供有关错误的有用信息。