如何在 MySQL 的多列中快速进行全文搜索?
How to do fulltext search in multiple columns in MySQL, quickly?
我知道这个问题已经被问过好几次了。但是,让我解释一下。
我有一个 table,其中包含 450k 条用户记录(ID、名字、姓氏、地址、phone 号码等)。
我想通过他们的名字 and/or 他们的姓氏来搜索用户。
我使用了这些查询:
SELECT * FROM correspondants WHERE nom LIKE 'Renault%' AND prénom LIKE 'r%';
和
SELECT * FROM correspondants WHERE CONCAT(nom, CHAR(32), prénom= LIKE 'Renault r%';
效果很好,但持续时间太长(1.5 秒)。这是我的问题。
为了修复它,我尝试在 'nom' 和 'prénom' 两个列上使用 MATCH 和 AGAINST 以及全文索引:
SELECT * FROM correspondants WHERE MATCH(nom, prénom) AGAINST('Renault r');
非常快(0,000 s ..)但是结果很糟糕,我没有得到我应该得到的。
例如,使用LIKE函数,结果为:
88623 RENAULT Rémy
91736 RENAULT Robin
202269 RENAULT Régine
(3 个结果)。
和 MATCH/AGAINST :
327380 RENAULT Luc
1559 RENAULT Marina
17280 RENAULT Anne
(...)
88623 RENAULT Rémy
91736 RENAULT Robin
202269 RENAULT Régine
(...)
436696 SEZNEC-RENAULT Helene
(...)
(115 个结果!)
使用 "AND" 搜索对两列进行快速高效的文本搜索的最佳方法是什么? (以及索引呢)
全文搜索不像 LIKE
字符串比较那样进行模式匹配。全文搜索只搜索完整的单词,而不搜索像 r%
.
这样的片段
还有一个字的最小大小,由 ft_min_word_len
配置变量控制。为避免使全文索引过大,它不会索引小于该变量的词。因此搜索时会忽略短词,因此 r
会被忽略。
在全文索引中也没有选择来搜索特定位置的单词,比如在字符串的开头。因此,您搜索 renault
可能会在字符串的中间找到。
要解决这些问题,您可以执行以下操作:
SELECT * FROM correspondants WHERE MATCH(nom, prénom) AGAINST('Renault')
AND CONCAT(nom, CHAR(32), prénom) LIKE 'Renault r%';
这将使用全文索引来查找 450,000 行中字符串中某处包含单词 renault
的一小部分。然后搜索中的第二项将在没有索引帮助的情况下完成,但仅针对与第一项匹配的行子集。
该特定查询最好以这种方式完成:
INDEX(nom, prénom)
WHERE non = 'Relault' AND prénom LIKE 'R%'
我建议您添加该索引 并 将代码添加到您的应用程序中以不同的方式处理不同的请求。
不要在函数调用中隐藏索引列,如CONCAT(nom, ...)
,将无法使用索引;相反,它将检查每一行,对每一行执行 CONCAT
,然后执行 LIKE
。很慢。
除了首字母的情况(如上所述),您应该尽量避免使用非常短的名字。但是,这是另一种情况,您可以使用额外的代码使其工作:
WHERE nom = 'Lu'
(具有相同的索引)。请注意,使用任何风格的 MATCH
都可能效率低得多。
因此,如果您有完整的姓氏,请使用 WHERE nom =
。如果给你一个前缀,那么使用 WHERE nom LIKE 'Prefix%'
等
可能会起作用
FULLTEXT
最适用于完整单词分散在较长文本中的情况,这不是您的情况,因为您将 nom
和 prénom
分开了。
也许您不应该将 MATCH
用于此架构中的任何内容 。
我知道这个问题已经被问过好几次了。但是,让我解释一下。
我有一个 table,其中包含 450k 条用户记录(ID、名字、姓氏、地址、phone 号码等)。 我想通过他们的名字 and/or 他们的姓氏来搜索用户。
我使用了这些查询:
SELECT * FROM correspondants WHERE nom LIKE 'Renault%' AND prénom LIKE 'r%';
和
SELECT * FROM correspondants WHERE CONCAT(nom, CHAR(32), prénom= LIKE 'Renault r%';
效果很好,但持续时间太长(1.5 秒)。这是我的问题。
为了修复它,我尝试在 'nom' 和 'prénom' 两个列上使用 MATCH 和 AGAINST 以及全文索引:
SELECT * FROM correspondants WHERE MATCH(nom, prénom) AGAINST('Renault r');
非常快(0,000 s ..)但是结果很糟糕,我没有得到我应该得到的。
例如,使用LIKE函数,结果为:
88623 RENAULT Rémy
91736 RENAULT Robin
202269 RENAULT Régine
(3 个结果)。
和 MATCH/AGAINST :
327380 RENAULT Luc
1559 RENAULT Marina
17280 RENAULT Anne
(...)
88623 RENAULT Rémy
91736 RENAULT Robin
202269 RENAULT Régine
(...)
436696 SEZNEC-RENAULT Helene
(...)
(115 个结果!)
使用 "AND" 搜索对两列进行快速高效的文本搜索的最佳方法是什么? (以及索引呢)
全文搜索不像 LIKE
字符串比较那样进行模式匹配。全文搜索只搜索完整的单词,而不搜索像 r%
.
还有一个字的最小大小,由 ft_min_word_len
配置变量控制。为避免使全文索引过大,它不会索引小于该变量的词。因此搜索时会忽略短词,因此 r
会被忽略。
在全文索引中也没有选择来搜索特定位置的单词,比如在字符串的开头。因此,您搜索 renault
可能会在字符串的中间找到。
要解决这些问题,您可以执行以下操作:
SELECT * FROM correspondants WHERE MATCH(nom, prénom) AGAINST('Renault')
AND CONCAT(nom, CHAR(32), prénom) LIKE 'Renault r%';
这将使用全文索引来查找 450,000 行中字符串中某处包含单词 renault
的一小部分。然后搜索中的第二项将在没有索引帮助的情况下完成,但仅针对与第一项匹配的行子集。
该特定查询最好以这种方式完成:
INDEX(nom, prénom)
WHERE non = 'Relault' AND prénom LIKE 'R%'
我建议您添加该索引 并 将代码添加到您的应用程序中以不同的方式处理不同的请求。
不要在函数调用中隐藏索引列,如CONCAT(nom, ...)
,将无法使用索引;相反,它将检查每一行,对每一行执行 CONCAT
,然后执行 LIKE
。很慢。
除了首字母的情况(如上所述),您应该尽量避免使用非常短的名字。但是,这是另一种情况,您可以使用额外的代码使其工作:
WHERE nom = 'Lu'
(具有相同的索引)。请注意,使用任何风格的 MATCH
都可能效率低得多。
因此,如果您有完整的姓氏,请使用 WHERE nom =
。如果给你一个前缀,那么使用 WHERE nom LIKE 'Prefix%'
等
FULLTEXT
最适用于完整单词分散在较长文本中的情况,这不是您的情况,因为您将 nom
和 prénom
分开了。
也许您不应该将 MATCH
用于此架构中的任何内容 。