mysql8.0 创建全文索引及mysql 8.0.32创建全文索引报错 Duplicate entry null-null的解决方案
mysql 8.0 创建全文索引
- mysql 8.0 创建全文索引
- 也可以创建复合全文索引,即多列全文索引
- 注意:启用mysql的WITH PARSER ngram 中文分词器,就会以ngram_token_size的最小分词长度为准,innodb_ft_min_token_size参数将失效
- 但有时候mysql 8.0.32创建全文索引报错 Duplicate entry null-null ,但是明明字段就没有null的值
- 如果想要删除全文索引
- 下面是全文索引模式详解
- 14.9.2 全文索引布尔模式
- InnoDB 布尔模式搜索的相关性排名
- 相关性排名是如何计算的
- 单字搜索的相关性排序
- Relevancy Ranking for a Multiple Word Search
- Natural Language Full-Text Searches
- MyISAM 的局限性
- 全文索引限制
mysql 8.0 创建全文索引
CREATE TABLE test_ngram (id INT PRIMARY KEY,content TEXT,FULLTEXT (content) WITH PARSER ngram
);INSERT INTO test_ngram VALUES (1, '数据库');
INSERT INTO test_ngram VALUES (2, '中效过滤排风机');-- 设置为当前数据库表,数据库名称会被转义,需要查询具体的数据名称
SET GLOBAL innodb_ft_aux_table = 'cost@002dxxxx@002ddev/test_ngram';
-- 查询具体的数据名称
SELECT table_id, name FROM information_schema.innodb_tables where `name` like '%test_ngram%';
-- 查看全文索引分词效果
SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE;explain select * from test_ngram where MATCH(content) AGAINST('*排风机*' IN BOOLEAN MODE)
也可以创建复合全文索引,即多列全文索引
WITH PARSER ngram 是使用mysql 内置的中文分词器,默认ngram_token_size分词为2
https://dev.mysql.com/doc/refman/8.0/en/fulltext-search-ngram.html
CREATE TABLE test_ngram (id INT PRIMARY KEY,title varchar(255),content TEXT,FULLTEXT (title,content) WITH PARSER ngram
);INSERT INTO test_ngram VALUES (1,'1', '数据库');
INSERT INTO test_ngram VALUES (2, '2','中效过滤排风机');explain select * from test_ngram where MATCH(content) AGAINST('*排风机*' IN BOOLEAN MODE)
注意:启用mysql的WITH PARSER ngram 中文分词器,就会以ngram_token_size的最小分词长度为准,innodb_ft_min_token_size参数将失效
SHOW VARIABLES LIKE 'innodb_ft_min_token_size'; -- 查看 InnoDB 最小分词长度为3
SHOW VARIABLES LIKE 'ngram%'; -- ngram_token_size查看 中文分词器最小分词长度为2
但有时候mysql 8.0.32创建全文索引报错 Duplicate entry null-null ,但是明明字段就没有null的值
这是mysqlmysql 8.0.32的一个bug
https://bugs.mysql.com/bug.php?id=110565
目前没有很好的解决办法,我在工作中的解决办法就是
-- 一个迂回的做法:table_name是你的数据表
-- 创建全文索引若是失败,先同步table_name的数据,先不创建全文索引
-- 当table_name的数据同步完成后,将table_name备份为table_name_back
-- 第一和②,选择其一即可:
-- 第一:直接复制表结构和数据(包含索引、约束等)
CREATE TABLE table_name_back AS SELECT * FROM table_name;
-- 第二:或者更严谨的方式(完整复制表结构 + 数据)
CREATE TABLE table_name_back LIKE table_name; -- 复制结构(含索引)
INSERT INTO table_name_back SELECT * FROM table_name; -- 导入数据
-- 清空数据
TRUNCATE table table_name
-- 开始加你想要的索引1、索引2
ALTER TABLE table_name
ADD FULLTEXT INDEX ft_name_spec (`name`, spec) WITH PARSER ngram;
-- 使用INSERT INTO...SELECT
INSERT INTO table_name SELECT * FROM table_name_back; -- 最后检查索引是否齐全了
如果想要删除全文索引
-- ALTER TABLE table_name drop index 全文索引名称
ALTER TABLE table_name drop index ft_name_spec
下面是全文索引模式详解
14.9.2 全文索引布尔模式
MySQL 可以使用IN BOOLEAN MODE
修饰符进行布尔全文搜索。使用此修饰符时,某些字符在搜索字符串中的单词开头或结尾具有特殊含义。在以下查询中,+
和-
运算符分别表示一个单词必须存在或不存在才能匹配。因此,该查询检索所有包含单词 “MySQL” 但不包含单词 “YourSQL” 的行。
mysql> SELECT * FROM articles WHERE MATCH (title,body)-> AGAINST ('+MySQL -YourSQL' IN BOOLEAN MODE);
+----+-----------------------+-------------------------------------+
| id | title | body |
+----+-----------------------+-------------------------------------+
| 1 | MySQL Tutorial | DBMS stands for DataBase ... |
| 2 | How To Use MySQL Well | After you went through a ... |
| 3 | Optimizing MySQL | In this tutorial, we show ... |
| 4 | 1001 MySQL Tricks | 1. Never run mysqld as root. 2. ... |
| 6 | MySQL Security | When configured properly, MySQL ... |
+----+-----------------------+-------------------------------------+
Note
在实现此功能时,MySQL 有时会使用所谓的隐含布尔逻辑,其中 “+” 代表 “与”,“-” 代表 “非”,[无运算符] 意味着 “或”。布尔全文搜索具有以下特点:它们不会自动按照相关性递减的顺序对行进行排序。
+
stands forAND
-
stands forNOT
- [no operator] implies
OR
布尔全文搜索具有以下特点:
-
它们不会自动按照相关性递减的顺序对行进行排序。
-
InnoDB
表在执行布尔查询时需要在MATCH()
表达式的所有列上有一个FULLTEXT
索引。针对MyISAM
搜索索引的布尔查询即使没有FULLTEXT
索引也可以工作,尽管以这种方式执行的搜索会非常缓慢。 -
最小和最大单词长度全文参数适用于使用内置全文解析器和 MeCab 解析器插件创建的
FULLTEXT
索引。innodb_ft_min_token_size
和innodb_ft_max_token_size
用于InnoDB
搜索索引。ft_min_word_len
和ft_max_word_len
用于MyISAM
搜索索引。最小和最大词长全文参数不适用于使用 ngram 解析器创建的
FULLTEXT
索引。ngram 标记大小由ngram_token_size
选项定义。 -
停用词列表适用于由
innodb_ft_enable_stopword
、innodb_ft_server_stopword_table
和innodb_ft_user_stopword_table
控制的InnoDB
搜索索引,以及由ft_stopword_file
控制的MyISAM
搜索索引。 -
InnoDB
全文搜索不支持在单个搜索词上使用多个运算符,如在这个例子中:'++apple'
。在单个搜索词上使用多个运算符会向标准输出返回语法错误。MyISAM 全文搜索成功处理相同的搜索,忽略除了紧邻搜索词的运算符之外的所有运算符。 -
InnoDB
全文搜索仅支持前导加号或减号。例如,InnoDB
支持'+apple'
,但不支持'apple+'
。指定尾随加号或减号会导致InnoDB
报告语法错误。 -
InnoDB
全文搜索不支持在通配符前使用正号('+*'
)、正负号组合('+-'
)或正负号组合开头('+-apple'
)。这些无效查询会返回语法错误。 -
InnoDB
全文搜索不支持在布尔全文搜索中使用@
符号。@
符号是为@distance
邻近搜索操作符保留的。 -
他们不使用适用于
MyISAM
搜索索引的 50% 阈值。
布尔全文搜索功能支持以下运算符:
-
+
前导或尾随的加号表示这个词必须出现在返回的每一行中。
InnoDB
仅支持前导加号。 -
-
一个前导或尾随的减号表示这个词必须不出现在任何返回的行中。
InnoDB
仅支持前导减号。一个前导或尾随的减号表示这个词必须不出现在任何返回的行中。InnoDB
仅支持前导减号。
注意:-
运算符仅用于排除那些被其他搜索词匹配的行。因此,仅包含以-
开头的词的布尔模式搜索会返回一个空结果。它不会返回 “除了包含任何排除词的行之外的所有行”。 -
(no operator)
默认情况下(既未指定
+
也未指定-
时),该词是可选的,但包含该词的行得分更高。这模仿了不带MATCH()AGAINST()
的IN BOOLEANMODE
修饰符时的行为。 -
@*
distance*
此操作符仅适用于
InnoDB
表。它测试两个或更多单词是否都在彼此特定距离内开始,距离以单词为单位进行测量。在@distance
操作符之前的双引号字符串中指定搜索词,例如,MATCH(col1) AGAINST('"word1word2 word3" @8' IN BOOLEAN MODE)
。 -
> <
这两个运算符用于更改单词对分配给行的相关值的贡献。
>
运算符增加贡献,<
运算符减少贡献。请参阅此列表后的示例。 -
( )
括号将单词分组为子表达式。带括号的组可以嵌套.
-
~
前导波浪号用作否定运算符,使单词对行的相关性贡献为负。这对于标记 “噪声” 单词很有用。包含这样一个单词的行的评级低于其他行,但不会像使用
-
运算符那样完全被排除。 -
*
星号用作截断(或通配符)运算符。与其他运算符不同,它被附加到要受影响的单词上。如果单词以
*
运算符前面的单词开头,则这些单词匹配。星号用作截断(或通配符)运算符。与其他运算符不同,它被附加到要受影响的单词上。如果单词以*
运算符前面的单词开头,则这些单词匹配。
如果使用截断运算符指定了一个单词,即使它太短或为停用词,它也不会从布尔查询中删除。一个单词是否太短是由innodb_ft_min_token_size
设置(对于InnoDB
表)或ft_min_word_len
(对于MyISAM
表)决定的。这些选项不适用于使用 ngram 解析器的FULLTEXT
索引。如果使用截断运算符指定了一个单词,即使它太短或为停用词,它也不会从布尔查询中删除。一个单词是否太短是由innodb_ft_min_token_size
设置(对于InnoDB
表)或ft_min_word_len
(对于MyISAM
表)决定的。这些选项不适用于使用 ngram 解析器的FULLTEXT
索引。
带通配符的单词被视为必须出现在一个或多个单词开头的前缀。如果最小单词长度为 4,搜索'+word +the*'
可能比搜索'+word +the'
返回更少的行,因为第二个查询忽略了太短的搜索词the
。. -
"
被双引号(
"
)括起来的短语仅匹配包含该短语(完全按照输入的字面内容)的行。全文搜索引擎将该短语拆分为单词,并在FULLTEXT
索引中搜索这些单词。非单词字符不需要完全匹配:短语搜索仅要求匹配结果包含与短语完全相同的单词且顺序相同。例如,"test phrase"
与"test,phrase"
匹配。被双引号("
)括起来的短语仅匹配包含该短语(完全按照输入的字面内容)的行。全文搜索引擎将该短语拆分为单词,并在FULLTEXT
索引中搜索这些单词。非单词字符不需要完全匹配:短语搜索仅要求匹配结果包含与短语完全相同的单词且顺序相同。例如,"test phrase"
与"test,phrase"
匹配。如果短语中没有任何单词在索引中,则结果为空。这些单词可能不在索引中的原因有多种:如果它们不在文本中、是停用词或者比索引单词的最小长度短.
以下示例展示了一些使用布尔全文运算符的搜索字符串。:
-
'apple banana'
查找包含这两个单词中至少一个的行.
-
'+apple +juice'
查找包含两个词的行.
-
'+apple macintosh'
查找包含单词 “apple” 的行,如果它们还包含 “macintosh”,则将这些行的排名提高。
-
'+apple -macintosh'
查找包含单词 “apple” 但不包含 “macintosh” 的行。.
-
'+apple ~macintosh'
查找包含单词 “apple” 的行,但如果该行也包含单词 “macintosh”,则其评分应低于不包含 “macintosh” 的行。这比搜索
'+apple-macintosh'
要 “宽松”,对于后者,“macintosh” 的存在会导致该行根本不会被返回。. -
'+apple +(>turnover <strudel)'
查找包含单词 “apple” 和 “turnover”,或者 “apple” 和 “strudel”(顺序不限)的行,但将 “appleturnover” 的排名置于 “apple strudel” 之上。
-
'apple*'
查找包含诸如“apple” “apples” “applesauce”或“applet”等单词的行。
-
'"some words"'
查找包含确切短语 “some words” 的行(例如,包含 “some words of wisdom” 但不包含 “some noise words” 的行)。请注意,包围短语的
"
字符是分隔短语的运算符字符。它们不是包围搜索字符串本身的引号。
InnoDB 布尔模式搜索的相关性排名
“InnoDB 布尔模式搜索的相关性排名”“InnoDB 布尔模式搜索的相关性排名”“InnoDB”全文搜索是基于“Sphinx”全文搜索引擎建模的,所使用的算法基于“BM25”和“TF-IDF”排名算法。由于这些原因,“InnoDB”布尔全文搜索的相关性排名可能与“MyISAM”的相关性排名不同。“InnoDB”全文搜索是基于“Sphinx”全文搜索引擎建模的,所使用的算法基于“BM25”和“TF-IDF”排名算法。由于这些原因,“InnoDB”布尔全文搜索的相关性排名可能与“MyISAM”的相关性排名不同。“InnoDB”使用“词频-逆文档频率”(“TF-IDF”)加权系统的变体来对给定全文搜索查询中文档的相关性进行排名。“TF-IDF”加权基于一个词在文档中出现的频率,并由该词在集合中的所有文档中出现的频率抵消。换句话说,一个词在文档中出现的频率越高,并且该词在文档集合中出现的频率越低,文档的排名就越高。
相关性排名是如何计算的
The term frequency (TF
) value is the number of times that a word appears in a document. The inverse document frequency (IDF
) value of a word is calculated using the following formula, where total_records
is the number of records in the collection, and matching_records
is the number of records that the search term appears in.
${IDF} = log10( ${total_records} / ${matching_records} )
When a document contains a word multiple times, the IDF value is multiplied by the TF value:
${TF} * ${IDF}
Using the TF
and IDF
values, the relevancy ranking for a document is calculated using this formula:
${rank} = ${TF} * ${IDF} * ${IDF}
The formula is demonstrated in the following examples.
单字搜索的相关性排序
This example demonstrates the relevancy ranking calculation for a single-word search.
mysql> CREATE TABLE articles (-> id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,-> title VARCHAR(200),-> body TEXT,-> FULLTEXT (title,body)->) ENGINE=InnoDB;
Query OK, 0 rows affected (1.04 sec)mysql> INSERT INTO articles (title,body) VALUES-> ('MySQL Tutorial','This database tutorial ...'),-> ("How To Use MySQL",'After you went through a ...'),-> ('Optimizing Your Database','In this database tutorial ...'),-> ('MySQL vs. YourSQL','When comparing databases ...'),-> ('MySQL Security','When configured properly, MySQL ...'),-> ('Database, Database, Database','database database database'),-> ('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),-> ('MySQL Full-Text Indexes', 'MySQL fulltext indexes use a ..');
Query OK, 8 rows affected (0.06 sec)
Records: 8 Duplicates: 0 Warnings: 0mysql> SELECT id, title, body, -> MATCH (title,body) AGAINST ('database' IN BOOLEAN MODE) AS score-> FROM articles ORDER BY score DESC;
+----+------------------------------+-------------------------------------+---------------------+
| id | title | body | score |
+----+------------------------------+-------------------------------------+---------------------+
| 6 | Database, Database, Database | database database database | 1.0886961221694946 |
| 3 | Optimizing Your Database | In this database tutorial ... | 0.36289870738983154 |
| 1 | MySQL Tutorial | This database tutorial ... | 0.18144935369491577 |
| 2 | How To Use MySQL | After you went through a ... | 0 |
| 4 | MySQL vs. YourSQL | When comparing databases ... | 0 |
| 5 | MySQL Security | When configured properly, MySQL ... | 0 |
| 7 | 1001 MySQL Tricks | 1. Never run mysqld as root. 2. ... | 0 |
| 8 | MySQL Full-Text Indexes | MySQL fulltext indexes use a .. | 0 |
+----+------------------------------+-------------------------------------+---------------------+
8 rows in set (0.00 sec)
There are 8 records in total, with 3 that match the “database” search term. The first record (id 6
) contains the search term 6 times and has a relevancy ranking of 1.0886961221694946
. This ranking value is calculated using a TF
value of 6 (the “database” search term appears 6 times in record id 6
) and an IDF
value of 0.42596873216370745, which is calculated as follows (where 8 is the total number of records and 3 is the number of records that the search term appears in):
${IDF} = LOG10( 8 / 3 ) = 0.42596873216370745
The TF
and IDF
values are then entered into the ranking formula:
${rank} = ${TF} * ${IDF} * ${IDF}
Performing the calculation in the MySQL command-line client returns a ranking value of 1.088696164686938.
mysql> SELECT 6*LOG10(8/3)*LOG10(8/3);
+-------------------------+
| 6*LOG10(8/3)*LOG10(8/3) |
+-------------------------+
| 1.088696164686938 |
+-------------------------+
1 row in set (0.00 sec)
Note
You may notice a slight difference in the ranking values returned by the SELECT ... MATCH ... AGAINST
statement and the MySQL command-line client (1.0886961221694946
versus 1.088696164686938
). The difference is due to how the casts between integers and floats/doubles are performed internally by InnoDB
(along with related precision and rounding decisions), and how they are performed elsewhere, such as in the MySQL command-line client or other types of calculators.
Relevancy Ranking for a Multiple Word Search
This example demonstrates the relevancy ranking calculation for a multiple-word full-text search based on the articles
table and data used in the previous example.
If you search on more than one word, the relevancy ranking value is a sum of the relevancy ranking value for each word, as shown in this formula:
${rank} = ${TF} * ${IDF} * ${IDF} + ${TF} * ${IDF} * ${IDF}
Performing a search on two terms (‘mysql tutorial’) returns the following results:
mysql> SELECT id, title, body, MATCH (title,body) -> AGAINST ('mysql tutorial' IN BOOLEAN MODE) AS score-> FROM articles ORDER BY score DESC;
+----+------------------------------+-------------------------------------+----------------------+
| id | title | body | score |
+----+------------------------------+-------------------------------------+----------------------+
| 1 | MySQL Tutorial | This database tutorial ... | 0.7405621409416199 |
| 3 | Optimizing Your Database | In this database tutorial ... | 0.3624762296676636 |
| 5 | MySQL Security | When configured properly, MySQL ... | 0.031219376251101494 |
| 8 | MySQL Full-Text Indexes | MySQL fulltext indexes use a .. | 0.031219376251101494 |
| 2 | How To Use MySQL | After you went through a ... | 0.015609688125550747 |
| 4 | MySQL vs. YourSQL | When comparing databases ... | 0.015609688125550747 |
| 7 | 1001 MySQL Tricks | 1. Never run mysqld as root. 2. ... | 0.015609688125550747 |
| 6 | Database, Database, Database | database database database | 0 |
+----+------------------------------+-------------------------------------+----------------------+
8 rows in set (0.00 sec)
In the first record (id 8
), ‘mysql’ appears once and ‘tutorial’ appears twice. There are six matching records for ‘mysql’ and two matching records for ‘tutorial’. The MySQL command-line client returns the expected ranking value when inserting these values into the ranking formula for a multiple word search:
mysql> SELECT (1*log10(8/6)*log10(8/6)) + (2*log10(8/2)*log10(8/2));
+-------------------------------------------------------+
| (1*log10(8/6)*log10(8/6)) + (2*log10(8/2)*log10(8/2)) |
+-------------------------------------------------------+
| 0.7405621541938003 |
+-------------------------------------------------------+
1 row in set (0.00 sec)
Note
The slight difference in the ranking values returned by the SELECT ... MATCH ... AGAINST
statement and the MySQL command-line client is explained in the preceding example.
Natural Language Full-Text Searches
默认情况下或使用IN NATURAL LANGUAGEMODE
修饰符时,MATCH()
函数针对文本集合对字符串执行自然语言搜索。集合是FULLTEXT
中包含的一列或多列。搜索字符串作为参数传递给AGAINST()
。对于表中的每一行,MATCH()
返回一个相关值;即搜索字符串与该行中在MATCH()
列表中命名的列中的文本之间的相似性度量。
mysql> CREATE TABLE articles (-> id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,-> title VARCHAR(200),-> body TEXT,-> FULLTEXT (title,body)-> ) ENGINE=InnoDB;
Query OK, 0 rows affected (0.08 sec)mysql> INSERT INTO articles (title,body) VALUES-> ('MySQL Tutorial','DBMS stands for DataBase ...'),-> ('How To Use MySQL Well','After you went through a ...'),-> ('Optimizing MySQL','In this tutorial, we show ...'),-> ('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),-> ('MySQL vs. YourSQL','In the following database comparison ...'),-> ('MySQL Security','When configured properly, MySQL ...');
Query OK, 6 rows affected (0.01 sec)
Records: 6 Duplicates: 0 Warnings: 0mysql> SELECT * FROM articles-> WHERE MATCH (title,body)-> AGAINST ('database' IN NATURAL LANGUAGE MODE);
+----+-------------------+------------------------------------------+
| id | title | body |
+----+-------------------+------------------------------------------+
| 1 | MySQL Tutorial | DBMS stands for DataBase ... |
| 5 | MySQL vs. YourSQL | In the following database comparison ... |
+----+-------------------+------------------------------------------+
2 rows in set (0.00 sec)
默认情况下,搜索以不区分大小写的方式执行。若要执行区分大小写的全文搜索,请对已索引的列使用区分大小写或二进制排序规则。例如,使用 “utf8mb4” 字符集的列可以分配 “utf8mb4_0900_as_cs” 或 “utf8mb4_bin” 排序规则,以便在全文搜索时对其进行区分大小写处理。当在 WHERE 子句中使用 MATCH () 时,如前面所示的示例,只要满足以下条件,返回的行将自动按照最高相关性首先进行排序:必须没有显式的 ORDER BY 子句;搜索必须使用全文索引扫描而不是表扫描;如果查询连接表,则全文索引扫描必须是连接中的最左侧非常量表。
When MATCH()
is used in a WHERE
clause, as in the example shown earlier, the rows returned are automatically sorted with the highest relevance first as long as the following conditions are met:
- There must be no explicit
ORDER BY
clause. - The search must be performed using a full-text index scan rather than a table scan.
- If the query joins tables, the full-text index scan must be the leftmost non-constant table in the join.
Given the conditions just listed, it is usually less effort to specify using ORDER BY
an explicit sort order when one is necessary or desired.
Relevance values are nonnegative floating-point numbers. Zero relevance means no similarity. Relevance is computed based on the number of words in the row (document), the number of unique words in the row, the total number of words in the collection, and the number of rows that contain a particular word.
当MATCH()在WHERE子句中使用时,如前面的示例所示,只要满足以下条件,返回的行将自动按照最高相关性优先排序: 必须没有显式的ORDER BY子句。 搜索必须使用全文索引扫描而不是表扫描。 如果查询连接表,则全文索引扫描必须是连接中最左边的非常量表。 鉴于刚刚列出的条件,当需要或期望显式排序顺序时,通常使用ORDER BY指定显式排序顺序会更省力。 相关性值是非负浮点数。零相关性意味着没有相似性。相关性是根据行(文档)中的单词数量、行中的唯一单词数量、集合中的总单词数量以及包含特定单词的行数来计算的。
Note
术语 “文档” 可以与术语 “行” 互换使用,这两个术语都指行的索引部分。术语 “集合” 指的是索引列,并且包含所有行。
To simply count matches, you could use a query like this:
mysql> SELECT COUNT(*) FROM articles-> WHERE MATCH (title,body)-> AGAINST ('database' IN NATURAL LANGUAGE MODE);
+----------+
| COUNT(*) |
+----------+
| 2 |
+----------+
1 row in set (0.00 sec)
You might find it quicker to rewrite the query as follows:
mysql> SELECT-> COUNT(IF(MATCH (title,body) AGAINST ('database' IN NATURAL LANGUAGE MODE), 1, NULL))-> AS count-> FROM articles;
+-------+
| count |
+-------+
| 2 |
+-------+
1 row in set (0.03 sec)
第一个查询做了一些额外的工作(按相关性对结果进行排序),但也可以基于WHERE
子句使用索引查找。如果搜索匹配的行数很少,索引查找可能会使第一个查询更快。第二个查询执行全表扫描,如果搜索词在大多数行中都存在,那么全表扫描可能比索引查找更快。对于自然语言全文搜索,MATCH () 函数中命名的列必须与表中的某些 FULLTEXT 索引中包含的列相同。对于前面的查询,请注意,MATCH () 函数中命名的列(title 和 body)与文章表的 FULLTEXT 索引定义中命名的列相同。要分别搜索标题或正文,您需要为每个列创建单独的 FULLTEXT 索引。
对于自然语言全文搜索,在[
MATCH()][0]
函数中命名的列必须是表中某个FULLTEXT
索引中包含的相同列。对于前面的查询,请注意在[
MATCH()][0]
函数中命名的列(title
和body
)与article
表的FULLTEXT
索引定义中命名的那些列相同。要分别搜索title
或body
,您需要为每个列创建单独的FULLTEXT
索引。
你也可以执行布尔搜索或带查询扩展的搜索。这些搜索类型在14.9.2 节 “布尔全文搜索”和14.9.3 节 “带查询扩展的全文搜索”中有描述。
使用索引的全文搜索在MATCH()
子句中只能命名来自单个表的列,因为索引不能跨越多个表。对于MyISAM
表,可以在没有索引的情况下进行布尔搜索(尽管速度较慢),在这种情况下,可以命名来自多个表的列。
前面的示例是一个基本说明,展示了如何使用MATCH()
函数,其中行按相关性递减的顺序返回。下一个示例展示了如何显式检索相关性值。返回的行未排序,因为SELECT
语句既不包含WHERE
也不包含ORDER BY
子句:
mysql> SELECT id, MATCH (title,body)-> AGAINST ('Tutorial' IN NATURAL LANGUAGE MODE) AS score-> FROM articles;
+----+---------------------+
| id | score |
+----+---------------------+
| 1 | 0.22764469683170319 |
| 2 | 0 |
| 3 | 0.22764469683170319 |
| 4 | 0 |
| 5 | 0 |
| 6 | 0 |
+----+---------------------+
6 rows in set (0.00 sec)
以下示例更加复杂。查询返回相关性值,并且还按照相关性降序对行进行排序。为实现此结果,请两次指定MATCH()
:一次在SELECT
列表中,一次在WHERE
子句中。这不会产生额外开销,因为 MySQL 优化器注意到这两个MATCH()
调用是相同的,并且仅调用一次全文搜索代码。
mysql> SELECT id, body, MATCH (title,body)-> AGAINST ('Security implications of running MySQL as root'-> IN NATURAL LANGUAGE MODE) AS score-> FROM articles-> WHERE MATCH (title,body) -> AGAINST('Security implications of running MySQL as root'-> IN NATURAL LANGUAGE MODE);
+----+-------------------------------------+-----------------+
| id | body | score |
+----+-------------------------------------+-----------------+
| 4 | 1. Never run mysqld as root. 2. ... | 1.5219271183014 |
| 6 | When configured properly, MySQL ... | 1.3114095926285 |
+----+-------------------------------------+-----------------+
2 rows in set (0.00 sec)
被双引号(")括起来的短语仅匹配包含该短语的行,完全按照输入的形式。全文搜索引擎将该短语拆分为单词,并在FULLTEXT索引中搜索这些单词。非单词字符不需要完全匹配:短语搜索仅要求匹配项包含与短语完全相同的单词且顺序相同。例如,"test phrase"与"test, phrase"匹配。如果短语中没有在索引中的单词,则结果为空。例如,如果所有单词要么是停用词,要么比索引单词的最小长度短,则结果为空。
MySQL 的全文实现将任何连续的真正单词字符(字母、数字和下划线)视为一个单词。该序列也可以包含撇号(‘),但不能连续出现多个。这意味着 aaa’bbb 被视为一个单词,而 aaa’'bbb 被视为两个单词。单词开头或结尾的撇号会被全文解析器删除;'aaa’bbb’将被解析为 aaa’bbb。
内置的FULLTEXT解析器通过查找特定分隔符字符来确定单词的起始和结束位置;例如,(空格)、,和.。如果单词之间没有分隔符(例如在中文中),内置的FULLTEXT解析器就无法确定一个单词的起始和结束位置。为了能够将这种语言中的单词或其他索引项添加到使用内置FULLTEXT解析器的全文检索索引中,必须对它们进行预处理,以便它们由某个任意分隔符分隔开。或者,可以使用 ngram 解析器插件(用于中文、日文或韩文)或 MeCab 解析器插件(用于日文)创建FULLTEXT索引。
可以编写一个插件来替换内置的全文解析器。有关详细信息,请参阅MySQL 插件 API。有关解析器插件的示例源代码,请参阅 MySQL 源代码发行版的plugin/fulltext
目录。
某些单词在全文搜索中会被忽略::
-
任何太短的单词都会被忽略。对于InnoDB搜索索引,全文搜索找到的单词默认最小长度为三个字符;对于MyISAM,则为四个字符。在创建索引之前,可以通过设置配置选项来控制这个截止长度:对于InnoDB搜索索引,可以设置innodb_ft_min_token_size配置选项;对于MyISAM,可以设置ft_min_word_len
Note
这种行为不适用于使用 ngram 解析器的FULLTEXT索引。对于 ngram 解析器,标记长度由ngram_token_size选项定义。
- 停用词列表中的单词将被忽略。停用词是像“the”或“some”这样非常常见的单词,被认为具有零语义价值。有一个内置的停用词列表,但它可以被用户定义的列表覆盖。InnoDB 搜索索引和 MyISAM 搜索索引的停用词列表及相关配置选项不同。对于 InnoDB 搜索索引,停用词处理由配置选项 innodb_ft_enable_stopword、innodb_ft_server_stopword_table 和 innodb_ft_user_stopword_table 控制;对于 MyISAM 搜索索引,由配置选项 ft_stopword_file 控制。请参阅第 14.9.4 节“全文停用词”以查看默认停用词列表以及如何更改它们。默认最小单词长度可以按照第 14.9.6 节“微调 MySQL 全文搜索”中所述进行更改。集合和查询中的每个正确单词都根据其在集合或查询中的重要性进行加权。因此,一个在许多文档中出现的单词权重较低,因为它在这个特定集合中的语义价值较低。相反,如果这个单词很罕见,它会获得更高的权重。单词的权重被组合起来以计算行的相关性。这种技术在大型集合中效果最佳。
See Section 14.9.4, “Full-Text Stopwords” to view default stopword lists and how to change them. The default minimum word length can be changed as described in Section 14.9.6, “Fine-Tuning MySQL Full-Text Search”.
请参阅第 14.9.4 节“全文停用词”以查看默认停用词列表以及如何更改它们。默认最小单词长度可以按照第 14.9.6 节“微调 MySQL 全文搜索”中所述进行更改。
MyISAM 的局限性
对于非常小的表,单词分布不能充分反映它们的语义价值,并且此模型有时可能会为MyISAM表上的搜索索引产生奇怪的结果。例如,尽管单词 “MySQL” 存在于前面所示的articles表的每一行中,但在MyISAM搜索索引中搜索该单词却没有结果:
mysql> SELECT * FROM articles-> WHERE MATCH (title,body)-> AGAINST ('MySQL' IN NATURAL LANGUAGE MODE);
Empty set (0.00 sec)
搜索结果为空,是因为单词 “MySQL” 在至少 50% 的行中都存在,因此实际上被视为停用词。这种过滤技术更适用于大型数据集,在大型数据集中,你可能不希望结果集从 1GB 的表中每隔一行返回一次;而对于小型数据集,它可能会导致流行术语的结果不佳。
50%的阈值在你首次尝试全文搜索以了解其工作原理时可能会让你感到惊讶,并且使InnoDB表更适合进行全文搜索实验。如果你创建一个MyISAM表并仅向其中插入一两行文本,那么文本中的每个单词都会出现在至少 50%的行中。因此,在表包含更多行之前,任何搜索都不会返回任何结果。需要绕过 50%限制的用户可以在InnoDB表上构建搜索索引,或者使用在“第 14.9.2 节,‘布尔全文搜索’”中解释的布尔搜索模式。
全文索引限制
仅对 InnoDB 和 MyISAM 表支持全文搜索。不支持对分区表进行全文搜索。请参阅“第 26.6 节,‘分区的限制和局限性’”。 全文搜索可用于大多数多字节字符集。但对于 Unicode,可使用 utf8mb3 或 utf8mb4 字符集,但不能使用 ucs2 字符集。虽然不能在 ucs2 列上使用 FULLTEXT 索引,但可以在没有此类索引的 ucs2 列上执行“IN BOOLEAN MODE”搜索。关于 utf8mb3 的说明也适用于 utf8mb4,关于 ucs2 的说明也适用于 utf16、utf16le 和 utf32。 像中文和日文这样的表意语言没有单词分隔符。因此,内置的全文解析器无法确定这些语言及其他类似语言中单词的开始和结束位置。 提供了一个支持中文、日文和韩文(CJK)的基于字符的 ngram 全文解析器,以及一个支持日文的基于词的 MeCab 解析器插件,可用于 InnoDB 和 MyISAM 表。 虽然在单个表中使用多种字符集是受支持的,但 FULLTEXT 索引中的所有列必须使用相同的字符集和排序规则。 MATCH()列列表必须与表的某个 FULLTEXT 索引定义中的列列表完全匹配,除非此 MATCH()在 MyISAM 表上处于“IN BOOLEAN MODE”。对于 MyISAM 表,可以在未索引的列上进行布尔模式搜索,尽管可能会很慢。 AGAINST()的参数必须是在查询评估期间恒定的字符串值。例如,这排除了表列,因为表列可能每行都不同。 从 MySQL 8.0.28 开始,MATCH()的参数不能使用汇总列。 对于 FULLTEXT 搜索,索引提示比非 FULLTEXT 搜索更受限制。请参阅“第 10.9.4 节,‘索引提示’”。 对于 InnoDB,涉及具有全文索引的列的所有 DML 操作(INSERT、UPDATE、DELETE)在事务提交时进行处理。例如,对于 INSERT 操作,插入的字符串被标记化并分解为单个单词。然后,在事务提交时,单个单词被添加到全文索引表中。因此,全文搜索仅返回已提交的数据。 “%”字符不是全文搜索支持的通配符。