性能 LIKE 包括通配符 (%)
Performance LIKE including wildcards (%)
如果我执行这个查询:
SELECT * FROM table1 WHERE name LIKE '%girl%'
它 returns 名称包含 'girl' 的所有记录。但是,由于 LIKE
语句中的第一个通配符 %
,它不能(或不)使用此处所述的索引:Mysql Improve Search Performance with wildcards (%%)
然后我将查询更改为:
SELECT * FROM table1 WHERE name LIKE 'girl%' OR name LIKE '%girl%'
在 OR
的左侧,我删除了通配符以便它可以使用索引。但性能获胜取决于 MySQL 如何评估查询。
因此我的问题是:当我添加 OR
语句时,我的查询性能会提高吗?
不,性能是一样的。由于 OR
,MySQL 仍然需要评估第一个条件 (LIKE '%girl%'
)。然后它可以使用索引评估第二个条件。当您 EXPLAIN
您的查询时,您可以看到此信息(mysql 将显示它仍然需要进行完整的 table 扫描,这意味着检查每一行):
EXPLAIN SELECT * FROM table1 WHERE name LIKE 'girl%' OR name LIKE '%girl%'
为了更好地处理这些类型的查询,您需要使用 Fulltext indexes 和特殊语法来查询它们。但 FT 指数表现不同,并不适合所有情况。
(此答案提供了评论的摘要,并与之前的一些注释相矛盾。)
前导通配符:
SELECT * FROM table1 WHERE name LIKE 'girl%' OR name LIKE '%girl%'
SELECT * FROM table1 WHERE name LIKE '%girl%'
其中任何一个都将执行 table 扫描并忽略任何索引。这既是因为领先的外卡和 OR
。 (它不会使用 'girl%' 的索引,与 @Marki555 所说的相反——不值得付出额外的努力。)
通过 LIKE 进行范围查询(无前导通配符):
SELECT * FROM table1 WHERE name LIKE 'girl%'
将可能按以下方式使用INDEX(name)
:
- 向下钻取该索引的 BTree 到第一个
name
,从 "girl" 开始;
- 向前扫描(在索引中)直到以 "girl" 开头的最后一行;
- 对于第 2 步中的每个项目,查看数据以获得
*
。
由于第 3 步的成本很高,优化器首先估计在第 2 步中需要触及多少行。如果超过 table 的 20%(大约),它将恢复为 table扫描。 (因此,我使用“可能”。)
"Covering index":
SELECT name FROM table1 WHERE name LIKE '%girl%'
这将始终使用INDEX(name)
。那是因为索引"covers"。也就是说,SELECT
中的所有列都在 INDEX
中找到。由于 INDEX
看起来和感觉上都像 table,因此扫描索引是执行查询的最佳方式。由于索引通常小于 table,因此索引扫描通常比 table 扫描快。
这里有一个不太明显的"covering index",但它只适用于InnoDB:
PRIMARY KEY(id)
INDEX(name)
SELECT id FROM table1 WHERE name LIKE '%girl%'
InnoDB 中的每个辅助键 (name)
都隐式包含 PK (id)
。因此索引看起来像 (name, id)
。因此 SELECT
中的所有列都在索引中。因此它是 "covering index"。因此它将使用索引并执行 "index scan".
A "covering index" 由出现在 EXPLAIN SELECT ...
中的 Using index
表示。
如果我执行这个查询:
SELECT * FROM table1 WHERE name LIKE '%girl%'
它 returns 名称包含 'girl' 的所有记录。但是,由于 LIKE
语句中的第一个通配符 %
,它不能(或不)使用此处所述的索引:Mysql Improve Search Performance with wildcards (%%)
然后我将查询更改为:
SELECT * FROM table1 WHERE name LIKE 'girl%' OR name LIKE '%girl%'
在 OR
的左侧,我删除了通配符以便它可以使用索引。但性能获胜取决于 MySQL 如何评估查询。
因此我的问题是:当我添加 OR
语句时,我的查询性能会提高吗?
不,性能是一样的。由于 OR
,MySQL 仍然需要评估第一个条件 (LIKE '%girl%'
)。然后它可以使用索引评估第二个条件。当您 EXPLAIN
您的查询时,您可以看到此信息(mysql 将显示它仍然需要进行完整的 table 扫描,这意味着检查每一行):
EXPLAIN SELECT * FROM table1 WHERE name LIKE 'girl%' OR name LIKE '%girl%'
为了更好地处理这些类型的查询,您需要使用 Fulltext indexes 和特殊语法来查询它们。但 FT 指数表现不同,并不适合所有情况。
(此答案提供了评论的摘要,并与之前的一些注释相矛盾。)
前导通配符:
SELECT * FROM table1 WHERE name LIKE 'girl%' OR name LIKE '%girl%'
SELECT * FROM table1 WHERE name LIKE '%girl%'
其中任何一个都将执行 table 扫描并忽略任何索引。这既是因为领先的外卡和 OR
。 (它不会使用 'girl%' 的索引,与 @Marki555 所说的相反——不值得付出额外的努力。)
通过 LIKE 进行范围查询(无前导通配符):
SELECT * FROM table1 WHERE name LIKE 'girl%'
将可能按以下方式使用INDEX(name)
:
- 向下钻取该索引的 BTree 到第一个
name
,从 "girl" 开始; - 向前扫描(在索引中)直到以 "girl" 开头的最后一行;
- 对于第 2 步中的每个项目,查看数据以获得
*
。
由于第 3 步的成本很高,优化器首先估计在第 2 步中需要触及多少行。如果超过 table 的 20%(大约),它将恢复为 table扫描。 (因此,我使用“可能”。)
"Covering index":
SELECT name FROM table1 WHERE name LIKE '%girl%'
这将始终使用INDEX(name)
。那是因为索引"covers"。也就是说,SELECT
中的所有列都在 INDEX
中找到。由于 INDEX
看起来和感觉上都像 table,因此扫描索引是执行查询的最佳方式。由于索引通常小于 table,因此索引扫描通常比 table 扫描快。
这里有一个不太明显的"covering index",但它只适用于InnoDB:
PRIMARY KEY(id)
INDEX(name)
SELECT id FROM table1 WHERE name LIKE '%girl%'
InnoDB 中的每个辅助键 (name)
都隐式包含 PK (id)
。因此索引看起来像 (name, id)
。因此 SELECT
中的所有列都在索引中。因此它是 "covering index"。因此它将使用索引并执行 "index scan".
A "covering index" 由出现在 EXPLAIN SELECT ...
中的 Using index
表示。