性能 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)

  1. 向下钻取该索引的 BTree 到第一个 name,从 "girl" 开始;
  2. 向前扫描(在索引中)直到以 "girl" 开头的最后一行;
  3. 对于第 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 表示。