编程

MySQL 全文检索

898 2023-04-08 23:45:00

MySQL 支持使用 LIKE 运算符和正则表达式进行文本搜索 。但是,当 text 列很大并且表中的行数增加时,使用这些方法有一些限制:

  • 性能:MySQL 必须扫描整个表,以根据 LIKE 正则表达式中的语句或模式中的模式查找确切的文本。
  • 灵活的搜索:通过 LIKE 运算符和正则表达式搜索,很难有灵活的搜索查询,例如,查找描述中包含 car 但不包含的产品 classic.
  • 相关性排名:无法指定结果集中哪一行与搜索项更相关。

由于这些限制,MySQL 扩展了一个非常好的功能,即所谓的全文搜索。从技术上讲,MySQL 根据启用的全文搜索列的单词创建索引,并对此索引执行搜索。MySQL 使用复杂的算法来确定与搜索查询匹配的行。

以下是 MySQL 全文搜索的一些重要功能:

  • SQL-LIKE 接口:您使用 SQL-LIKE 语句来使用全文搜索。
  • 完全动态索引:只要列的数据发生更改,MySQL 就会自动更新文本列的索引。
  • 中等索引大小:存储索引不需要太多内存。
  • 最后但并非最不重要的是,基于复杂的搜索查询搜索速度很快。

注意:并非所有存储引擎都支持全文搜索功能。在 MySQL 5.6 或更高版本中,只有 MyISAM 和 InnoDB 存储引擎支持全文搜索。

在表的列中执行全文搜索之前,必须将其数据编入索引。只要列的数据发生变化,MySQL 就会重新创建全文索引。在 MySQL 中,全文索引是一种具有名称 FULLTEXT 的索引

MySQL 支持 为启用全文搜索的列自动索引和重新索引数据  。MySQL 的 5.6 或更高版本允许你定义一个全文索引的数据类型为一列 CHAR,VARCHAR 或 TEXT 在 MyISAM 中或 InnoDB 的表型。请注意,自 5.6 版以来,MySQL 支持 InnoDB 表中的全文索引。

MySQL 允许你在为现有表创建表或 ALTER TABLE 或 CREATE INDEX 语句时使用 CREATE TABLE 语句定义 FULLTEXT 索引。

使用 CREATE TABLE 语句定义 FULLTEXT 索引

通常,FULLTEXT 使用以下 CREATE TABLE 语句创建新表时,可以为列定义索引:

CREATE TABLE table_name(
 column1 data_type, 
        column2 data_type,
        column3 data_type,
 …
PRIMARY_KEY(key_column),
FULLTEXT (column1,column2,..)
); 

要创建 FULLTEXT 索引,请在 FULLTEXT 关键字后面的括号中放置逗号分隔列的列表。

以下语句创建一个名为 posts 具有 FULLTEXT 包含 post_content 列的索引的新表。

CREATE TABLE posts (
  id int(4) NOT NULL AUTO_INCREMENT,
  title varchar(255) NOT NULL,
  post_content text,
  PRIMARY KEY (id),
  FULLTEXT KEY post_content (post_content)
); 

为现有表定义 FULLTEXT 索引

如果您已有现有表并想要定义全文索引,则可以使用 ALTER TABLE 语句或 CREATE INDEX 语句。

使用 ALTER TABLE 语句定义 FULLTEXT 索引

以下语法 FULLTEXT 使用以下语句定义索引 ALTER TABLE:

ALTER TABLE table_name  
ADD FULLTEXT(column_name1, column_name2,…) 

在此语法中,你将使用 table_name 和 ADD FULLTEXT 子句定义 FULLTEXT 一个或多个列的索引。

例如,你可以 FULLTEXT 为示例数据库的表中的 productDescription 和 productLine 列定义索引,如下所示:products

ALTER TABLE products  
ADD FULLTEXT(productDescription,productLine); 

使用 CREATE INDEX 语句定义 FULLTEXT 索引

您还可以使用 CREATE INDEX 语句为现有表创建 FULLTEXT 索引。请参阅以下语法:

CREATE FULLTEXT INDEX index_name
ON table_name(idx_column_name,...) 

以下语句 FULLTEXT 为表的列 addressLine1 和 addressLine2 列创建索引 offices。

CREATE FULLTEXT INDEX address
ON offices(addressLine1,addressLine2); 

请注意,对于具有多行的表,将数据加载到 FULLTEXT 首先没有索引的表中然后创建 FULLTEXT 索引比将大量数据加载到具有现有 FULLTEXT 索引的表中更快。

删除全文搜索列

要删除 FULLTEXT 索引,只需使用 ALTER TABLE … DROP INDEX 语句删除索引即可。例如,以下语句删除表中的索引:address FULLTEXT offices

ALTER TABLE offices
DROP INDEX address; 

MySQL 自然语言全文搜索简介

在自然语言全文搜索中,MySQL 查找与自由文本自然人类语言查询相关的行或文档,例如,“如何使用 MySQL 自然语言全文搜索”。

相关性是一个正浮点数。当相关性为零时,意味着没有相似性。MySQL 根据各种因素计算相关性,包括文档中的单词数,文档中唯一单词的数量,集合中单词的总数以及包含特定单词的文档(行)数。

要执行自然语言全文搜索,请使用 MATCH() 和 AGAINST() 运行。MATCH() 函数指定要搜索的列,AGAINST() 函数确定要使用的搜索表达式。

MySQL 自然语言全文搜索示例

我们将使用示例数据库中的 products 表进行演示。

+--------------------+
| products           |
+--------------------+
| productCode        |
| productName        |
| productLine        |
| productScale       |
| productVendor      |
| productDescription |
| quantityInStock    |
| buyPrice           |
| MSRP               |
+--------------------+
9 rows in set (0.00 sec)

首先,你需要 使用以下 ALTER TABLE ADD FULLTEXT语句在 products 表的 productLine 列中启用全文搜索:

ALTER TABLE products 
ADD FULLTEXT(productline); 

其次,你可以搜索其产品系列包含术语的产品 Classic 。你使用 MATCH() 和 AGAINST() 函数作为以下查询:

SELECT productName, productline
FROM products
WHERE MATCH(productline) AGAINST('Classic'); 
+-------------------------------------+--------------+
| productName                         | productline  |
+-------------------------------------+--------------+
| 1952 Alpine Renault 1300            | Classic Cars |
| 1972 Alfa Romeo GTA                 | Classic Cars |
| 1962 LanciaA Delta 16V              | Classic Cars |
| 1968 Ford Mustang                   | Classic Cars |
| 2001 Ferrari Enzo                   | Classic Cars |
| 1969 Corvair Monza                  | Classic Cars |
| 1968 Dodge Charger                  | Classic Cars |
| 1969 Ford Falcon                    | Classic Cars |
| 1970 Plymouth Hemi Cuda             | Classic Cars |
| 1969 Dodge Charger                  | Classic Cars |
...

要搜索其产品系列包含 Classic 或 Vintage 术语的产品,你可以执行以下查询:

SELECT productName, productline
FROM products
WHERE MATCH(productline) AGAINST('Classic,Vintage');
 +-------------------------------------------+--------------+
| productName                               | productline  |
+-------------------------------------------+--------------+
| 1937 Lincoln Berline                      | Vintage Cars |
| 1936 Mercedes-Benz 500K Special Roadster  | Vintage Cars |
| 1917 Grand Touring Sedan                  | Vintage Cars |
| 1911 Ford Town Car                        | Vintage Cars |
| 1932 Model A Ford J-Coupe                 | Vintage Cars |
| 1928 Mercedes-Benz SSK                    | Vintage Cars |
| 1913 Ford Model T Speedster               | Vintage Cars |
| 1934 Ford V8 Coupe                        | Vintage Cars |
| 18th Century Vintage Horse Carriage       | Vintage Cars |
| 1903 Ford Model A                         | Vintage Cars |
| 1917 Maxwell Touring Car                  | Vintage Cars |
| 1941 Chevrolet Special Deluxe Cabriolet   | Vintage Cars |
| 1932 Alfa Romeo 8C2300 Spider Sport       | Vintage Cars |
| 1904 Buick Runabout                       | Vintage Cars |
| 1939 Cadillac Limousine                   | Vintage Cars |
| 1939 Chevrolet Deluxe Coupe               | Vintage Cars |
| 1938 Cadillac V-16 Presidential Limousine | Vintage Cars |
| 1912 Ford Model T Delivery Wagon          | Vintage Cars |
| 1937 Horch 930V Limousine                 | Vintage Cars |
| 1940 Ford Delivery Sedan                  | Vintage Cars |
| 1936 Mercedes Benz 500k Roadster          | Vintage Cars |
| 1936 Chrysler Airflow                     | Vintage Cars |
| 1928 Ford Phaeton Deluxe                  | Vintage Cars |
| 1930 Buick Marquette Phaeton              | Vintage Cars |
| 1952 Alpine Renault 1300                  | Classic Cars |
| 1972 Alfa Romeo GTA                       | Classic Cars |
...

AGAINST() 函数默认使用 IN NATURAL LANGUAGE MODE 搜索修饰符,因此你可以在查询中省略它。还有其他搜索修饰符,例如,IN BOOLEAN MODE 用于布尔文本搜索

你可以在查询中明确使用 IN NATURAL LANGUAGE MODE 搜索修饰符,如下所示:

SELECT productName, productline
FROM products
WHERE MATCH(productline) 
AGAINST('Classic,Vintage' IN NATURAL LANGUAGE MODE); 

默认情况下,MySQL 以不区分大小写的方式执行搜索。但是,你可以指示 MySQL 使用二进制排序规则对索引列执行区分大小写的搜索。

按相关性对结果集进行排序

全文搜索的一个非常重要的特性是 MySQL 如何根据结果集的相关性对结果集中的行进行排名。MATCH() 在 WHERE 子句中使用函数时,MySQL 首先返回更相关的行。

以下示例显示 MySQL 如何按相关性对结果集进行排序。

首先,为表的 productName 列启用全文搜索功能 products。

ALTER TABLE products 
ADD FULLTEXT(productName); 

其次,搜索名称包含 Ford 和/或 1932 使用以下查询的产品:

SELECT productName, productline
FROM products
WHERE MATCH(productName) AGAINST('1932,Ford'); 

让我们来看看输出:

+-------------------------------------+------------------+
| productName                         | productline      |
+-------------------------------------+------------------+
| 1932 Model A Ford J-Coupe           | Vintage Cars     |
| 1932 Alfa Romeo 8C2300 Spider Sport | Vintage Cars     |
| 1968 Ford Mustang                   | Classic Cars     |
| 1969 Ford Falcon                    | Classic Cars     |
| 1940 Ford Pickup Truck              | Trucks and Buses |
| 1911 Ford Town Car                  | Vintage Cars     |
| 1926 Ford Fire Engine               | Trucks and Buses |
| 1913 Ford Model T Speedster         | Vintage Cars     |
| 1934 Ford V8 Coupe                  | Vintage Cars     |
| 1903 Ford Model A                   | Vintage Cars     |
| 1976 Ford Gran Torino               | Classic Cars     |
| 1940s Ford truck                    | Trucks and Buses |
| 1957 Ford Thunderbird               | Classic Cars     |
| 1912 Ford Model T Delivery Wagon    | Vintage Cars     |
| 1940 Ford Delivery Sedan            | Vintage Cars     |
| 1928 Ford Phaeton Deluxe            | Vintage Cars     |
+-------------------------------------+------------------+
16 rows in set (0.00 sec)

产品其名称中包含两个1932 和 Ford 先返回,然后他们的名字中包含的不仅是产品 Ford 关键字。

使用全文搜索时,你应记住以下几点:

  • 在 MySQL 全文搜索引擎定义的搜索词的最小长度为 4。这意味着,如果你搜索其长度小于 4 例如关键字 car,cat 等等,你不会得到任何结果。
  • 停用词被忽略。MySQL 定义了 MySQL 源代码分发中的停用词列表 storage/myisam/ft_static.c

在本教程中,已经向你展示了如何使用 MATCH() 和 AGAINST() 函数在 MySQL 中执行自然语言搜索。