0

以下查询很快:

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  

谁能解释为什么用“或”做“加入”语句比加入两个表要慢得多?

4

3 回答 3

5

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

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

您可以阅读更多关于此处使用的算法的信息。

您还可以检查Stan McGeek提供的EXPLAIN小提琴中的输出

更新:还请记住:

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

于 2013-06-28T07:26:42.787 回答
2

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

假设香蕉有两个索引,一个 on banana.one,另一个 on banana.two

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

在第二个版本中,只有一个JOIN和一个单通道。但是 aJOIN只能使用一个索引。由于两个索引都不充分(仅涵盖两个JOIN条件之一),因此它不会使用任何索引并进行全表扫描banana(复杂度 = NxMx2 = NxM,其中 M = 香蕉数)。

您可以使用EXPLAIN SELECT ...每个查询中的一个来检查这一点。

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

于 2013-06-29T00:00:08.293 回答
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

似乎正在寻找所有applesbananabanana.onebanana.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' 的外键列onetwo.

如果你在香蕉列上有一个覆盖索引onetwo例如

create index banana_one_two on banana ( one , two )

你表现不佳的查询慢的也希望工作正常。

检查您获得的执行计划可能会为您提供有关问题所在的有用信息。

于 2013-06-29T00:11:49.137 回答