以下查询很快:
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”语句比加入两个表要慢得多?
答案 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
似乎正在查找列apples
和banana
上没有匹配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
的主键id
和banana
的外键列one
上有索引,任何体面的查询优化器都应该可以做到这一点。和two
。
如果您在香蕉列one
和two
上有覆盖索引,例如
create index banana_one_two on banana ( one , two )
你表现不佳的查询也很慢。
检查您获得的执行计划可能会为您提供有关错误的有用信息。