提问人:Martin AJ 提问时间:6/9/2016 最后编辑:AugusteMartin AJ 更新时间:10/11/2023 访问量:2783
当条件之间有OR时,MySQL可以使用索引吗?
Can MySQL use Indexes when there is OR between conditions?
问:
我有两个查询加上它自己的结果:EXPLAIN
一:
SELECT *
FROM notifications
WHERE id = 5204 OR seen = 3
基准(10,000 行):0.861
二:
SELECT h.* FROM ((SELECT n.* from notifications n WHERE id = 5204)
UNION ALL
(SELECT n.* from notifications n WHERE seen = 3)) h
基准(10,000 行):2.064
上述两个查询的结果是相同的。此外,我还有以下两个索引:notifications
notifications(id) -- this is PK
notification(seen)
如您所知,通常会阻止索引的有效使用,这就是我编写第二个查询(由 )。但是经过一些测试,我发现它仍然使用比使用快得多。所以我很困惑,我真的无法选择最适合我的选择。OR
UNION
OR
UNION
根据一些合乎逻辑的合理解释,使用更好,但基准测试的结果说使用更好。如果我使用哪种方法,请您帮助我吗?union
OR
答:
该案例的查询计划似乎表明它确实使用了索引,因此显然是可以的,至少在这种情况下是这样。这似乎是完全合理的,因为 上有一个索引,并且是 PK。OR
MySQL
seen
id
根据一些合乎逻辑和合理的解释,使用联合更好,但基准测试的结果说使用 OR 更好。
如果“合乎逻辑和合理的解释”与现实相矛盾,那么可以安全地假设逻辑是有缺陷的,或者解释是错误的或不适用的。众所周知,性能很难预测;在速度很重要的情况下,性能测试是必不可少的。
如果我使用哪种方法,请您帮助我吗?
您应该使用对输入进行更快测试的那个,该输入可以充分模拟程序在实际使用中看到的内容。
但是,还要注意,您的两个查询在语义上并不等效:如果 also has 的行,则查询将返回一次,但查询将返回两次。在正确的代码和不正确的代码之间进行选择是没有意义的,除非哪一个是正确的。id = 5204
seen = 3
OR
UNION ALL
评论
id = 5204
seen = 3
UNION ALL
UNION
OR
OR
UNION ALL
OR
UNION
UNION
答案包含在您的问题中。OR 的 EXPLAIN 输出表示 - 这意味着正在使用优化,并且查询实际上是通过合并两个索引的结果来执行的。Using union(PRIMARY, seen)
index_merge
因此,MySQL在某些情况下可以使用索引,并且在这种情况下可以使用索引。但是并不总是可用或不使用,因为索引的统计数据表明它不值得。在这些情况下,OR 可能比 UNION 慢得多(或者不是,如果您不确定,您需要始终检查两个版本)。index_merge
在您的测试中,您“很幸运”,MySQL自动为您进行了正确的优化。情况并非总是如此。
index_merge
顾名思义,使用 or for 和条件适当地组合两个索引的主键,然后通过 PK 查找表中的其余值。Sort Merge Join
Sort Merge Union
AND
OR
为此,两个索引上的条件应使每个索引按顺序生成主键(您的条件是)。
您可以在文档中找到条件的严格定义,但简而言之,您应该使用相等条件按索引的所有部分进行过滤,可能加上 、 或 PK。<
=
>
如果您有索引,这应该是(方括号中的部分不是必需的)。(col1, col2, col3)
col1 = :val1 AND col2 = :val2 AND col3 = :val3 [ AND id > :id ]
以下情况将不起作用:
col1 = :val1 -- you omit col2 and col3
col1 = :val1 AND col2 = :val2 AND col3 > :val3 -- you can only use equality on key parts
作为免费的副作用,您的输出按 排序。id
您可以使用以下方法获得类似的结果:
SELECT *
FROM (
SELECT 5204 id
UNION ALL
SELECT id
FROM mytable
WHERE seen = 3
AND id <> 5204
) q
JOIN mytable m
ON m.id = q.id
,除了在早期版本的 MySQL 中,派生表必须具体化,这肯定会使查询性能变差,并且您的结果将不再按排序。id
简而言之,如果您的查询允许,那就去做吧。index_merge(union)
评论
index_merge
. . . WHERE col1 = :val1
(col1, col2, col3)
col1
index_merge
index_merge
index_merge
index_merge
(这个问题已经很老了,但我觉得有必要指出一些缺陷——无论是当时还是现在。
这两个查询结果不一定相同。将避免任何重复;将 [可能] 显示一行两次(一次表示,一次表示 .
OR
UNION ALL
id=5204
seen=3
较新版本的MySQL将更好地优化,避免使用临时表来收集每个子查询的结果。(此优化有局限性。 继续需要临时表。(注意“使用临时”)
UNION ALL
UNION DISTINCT
“索引合并并集”很少发生,很难预测何时使用。“索引合并相交”几乎总是表示复合索引会使查询速度更快。
SELECT *
是放缓的一部分。执行以下操作可能会更好。它更有效地获取 id,返回表以获取可能笨重的“*”。如果有大列,这也许可以解释为什么这么慢。UNION
SELECT * FROM ( fetch just `id` from the OR or UNION or whatever ) AS x JOIN tbl USING (id);
评论
SELECT h.* FROM (
) h
UNION
UNION ALL
UNION
id <> 5204
seen
UNION ALL