MySQL 对多个 OR 使用索引,但对 IN 没有索引而且速度慢得多
MySQL using Indexes for multiple ORs, but no index for IN and so much slower
我一直忙于更改一些 SQL 查询,以便它们看起来更易于人眼阅读,我还被告知它们可能会快 5-10%。
之前的 SQL 语句看起来像这样。
SELECT * FROM teams WHERE Team1='Joe Bloggs' OR Team2='Joe Bloggs' OR
Team3='Joe Bloggs'
我改成了
SELECT * FROM teams WHERE 'Joe Bloggs' IN (Team1,Team2,Team3)
新查询大约慢了 10 倍,在检查了可能的原因后我发现它没有使用任何索引,即使我试图强制索引它仍然不会使用它。
table 有大约 120,000 行,我无法更改 table 格式,因为我无法访问其他应用程序,请使用它。 Team1、Team2、Team3 列都是 VARCHAR(45)
谁能解释为什么索引用于原始查询而不用于新查询?我读了很多页但找不到答案,我读到 mysql 可能确定不使用索引会更快,但是这里不应该是这种情况,因为 IN 查询是几乎慢了 10 倍。
多个 OR SELECT(运行 1000 次,没有缓存)- 12.863906860352 已过去
IN SELECT(运行 1000 次,无缓存)- 122.73787903786 已用
感谢您的宝贵时间。
查询中:
SELECT * FROM teams WHERE 'Joe Bloggs' IN (Team1,Team2,Team3)
您正在比较(查找)一堆列与字符串文字。优化器通常会在搜索目标上使用索引,在本例中为 Joe Bloggs
,以在 IN
子句中查找值。但是,它不能在字符串文字上放置索引。所以,这里一切都颠倒过来了,这就是索引无济于事的原因。
另一方面,在您的第一个查询中:
SELECT * FROM teams WHERE Team1='Joe Bloggs' OR Team2='Joe Bloggs' OR Team3='Joe Bloggs'
MySQL 会抓取字符串文字,然后使用 B 树索引在各个列中查找它们。这与您预期和看到的一样。
我不知道为什么性能会有所不同——在这两种情况下似乎都不会使用索引。
您可以这样写查询:
SELECT t.*
FROM teams t
WHERE Team1 = 'Joe Bloggs'
UNION ALL
SELECT t.*
FROM teams t
WHERE Team2 = 'Joe Bloggs' AND Team1 <> 'Joe Bloggs'
UNION ALL
SELECT t.*
FROM teams t
WHERE Team3 = 'Joe Bloggs'
AND Team2 <> 'Joe Bloggs'
AND Team1 <> 'Joe Bloggs';
这可以利用 (Team1)
、(Team2, Team1)
和 (Team3, Team2, Team1)
上的索引。
你有一个“inverted IN”;优化器只会为 column in (value1, value2, value3)
.
使用索引
但是,如果您在 3 列的每一列上都有单独的索引,还有另一种方法应该会产生比您的任何一种尝试都好得多的性能:
SELECT * FROM teams WHERE Team1='Joe Bloggs'
UNION
SELECT * FROM teams WHERE Team2='Joe Bloggs'
UNION
SELECT * FROM teams WHERE Team3='Joe Bloggs'
table会查询3次,但每次都会用到索引
如果您确定不会有任何欺骗,或者您不介意欺骗,请将 UNION
更改为 UNION ALL
以进一步加快速度(UNION
额外开销或重复数据删除)。
方案 A:使用 FULLTEXT (team1, team2, team3)
和 MATCH(team1, team2, team3) AGAINST ('+Joe +Briggs' IN BOOLEAN MODE)
。使用这种方法有很多注意事项,但是,如果它适用于您的情况,它将非常快。
B 计划:尽管 "cannot change the table format",您可以玩一些带有 VIEW 的游戏,以避免跨列展开数组(团队)。
我一直忙于更改一些 SQL 查询,以便它们看起来更易于人眼阅读,我还被告知它们可能会快 5-10%。
之前的 SQL 语句看起来像这样。
SELECT * FROM teams WHERE Team1='Joe Bloggs' OR Team2='Joe Bloggs' OR Team3='Joe Bloggs'
我改成了
SELECT * FROM teams WHERE 'Joe Bloggs' IN (Team1,Team2,Team3)
新查询大约慢了 10 倍,在检查了可能的原因后我发现它没有使用任何索引,即使我试图强制索引它仍然不会使用它。
table 有大约 120,000 行,我无法更改 table 格式,因为我无法访问其他应用程序,请使用它。 Team1、Team2、Team3 列都是 VARCHAR(45)
谁能解释为什么索引用于原始查询而不用于新查询?我读了很多页但找不到答案,我读到 mysql 可能确定不使用索引会更快,但是这里不应该是这种情况,因为 IN 查询是几乎慢了 10 倍。
多个 OR SELECT(运行 1000 次,没有缓存)- 12.863906860352 已过去 IN SELECT(运行 1000 次,无缓存)- 122.73787903786 已用
感谢您的宝贵时间。
查询中:
SELECT * FROM teams WHERE 'Joe Bloggs' IN (Team1,Team2,Team3)
您正在比较(查找)一堆列与字符串文字。优化器通常会在搜索目标上使用索引,在本例中为 Joe Bloggs
,以在 IN
子句中查找值。但是,它不能在字符串文字上放置索引。所以,这里一切都颠倒过来了,这就是索引无济于事的原因。
另一方面,在您的第一个查询中:
SELECT * FROM teams WHERE Team1='Joe Bloggs' OR Team2='Joe Bloggs' OR Team3='Joe Bloggs'
MySQL 会抓取字符串文字,然后使用 B 树索引在各个列中查找它们。这与您预期和看到的一样。
我不知道为什么性能会有所不同——在这两种情况下似乎都不会使用索引。
您可以这样写查询:
SELECT t.*
FROM teams t
WHERE Team1 = 'Joe Bloggs'
UNION ALL
SELECT t.*
FROM teams t
WHERE Team2 = 'Joe Bloggs' AND Team1 <> 'Joe Bloggs'
UNION ALL
SELECT t.*
FROM teams t
WHERE Team3 = 'Joe Bloggs'
AND Team2 <> 'Joe Bloggs'
AND Team1 <> 'Joe Bloggs';
这可以利用 (Team1)
、(Team2, Team1)
和 (Team3, Team2, Team1)
上的索引。
你有一个“inverted IN”;优化器只会为 column in (value1, value2, value3)
.
但是,如果您在 3 列的每一列上都有单独的索引,还有另一种方法应该会产生比您的任何一种尝试都好得多的性能:
SELECT * FROM teams WHERE Team1='Joe Bloggs'
UNION
SELECT * FROM teams WHERE Team2='Joe Bloggs'
UNION
SELECT * FROM teams WHERE Team3='Joe Bloggs'
table会查询3次,但每次都会用到索引
如果您确定不会有任何欺骗,或者您不介意欺骗,请将 UNION
更改为 UNION ALL
以进一步加快速度(UNION
额外开销或重复数据删除)。
方案 A:使用 FULLTEXT (team1, team2, team3)
和 MATCH(team1, team2, team3) AGAINST ('+Joe +Briggs' IN BOOLEAN MODE)
。使用这种方法有很多注意事项,但是,如果它适用于您的情况,它将非常快。
B 计划:尽管 "cannot change the table format",您可以玩一些带有 VIEW 的游戏,以避免跨列展开数组(团队)。