MySQL-全文本搜索(学习如何使用MySQL的全文本搜索功能进行高级的数据查询。)

时间:2022-08-31 15:19:26

  并非所有引擎都支持全文本搜索MySQL。与所有其他的DBMS一样,MySQL具有一个具体管理和处理数据的内部引擎。在你使用CREATE TABLE语句时,该引擎具体创建表,而在你使用SELECT语句或进行其它的数据库处理时,该引擎在内部处理你的请求。多数时候,该引擎都隐藏在DBMS内,不需要过多关注它。

  但MySQL与其它的DBMS不一样,它具有多种引擎。它打包多个引擎,这些引擎都隐藏在MySQL服务器内,全都能执行CREATE TABLE与SELECT命令。

  为什么要发行多种引擎呢?因为它们具有不同的功能和特性,为不同的任务选择正确的引擎获得良好的功能和灵活性。

  当然,你可以忽略这些数据库引擎。如果省略ENGINE=语句,则使用默认引擎(很可能是MyISAM),多数SQL语句都会默认使用它。但不是所有的语句都默认使用它,这就是为什么ENGINE=语句很重要的原因。

  以下是几个需要知道的引擎:

  1.InnoDB是一个可靠的事务处理引擎,它不支持全文本搜索。

  2.MEMORY在功能等同于MyISAM,但由于数据存储在内存(不是磁盘)中,速度很快(特别适合于临时表)

  3.MyISAM是一个性能极高的引擎,它支持全文本搜索,但不支持事务处理。

  所支持引擎的完整列表(及它们之间的不同),请参阅(http://dev.mysql.com/doc/refman/5.0/en/storage_engines.html)

 

  LIKE关键字,利用通配操作符匹配文本(和部分文本)。使用LIKE,能够查找包含特殊值或部分值得行(不管这些值位于列内的什么位置)。

  用基于文本的搜索作为正则表达式匹配列值,使用正则表达式,可以编写查找所需行的非常复杂的匹配模式。

  虽然这些搜索机制非常有用,但存在几个重要的机制。

  1.性能:通配符和正则表达式匹配通常要求MySQL尝试匹配表中所有行(而且这些搜索极少使用表索引)。因此,由于被搜索行数不断增加,这些搜索可能非常耗时。

  2.明确控制:使用通配符和正则表达式匹配,很难(而且并不总是能)明确地控制匹配什么和不匹配什么。例如,指定一个词匹配和指定一个词不匹配....

  3.智能化的结果:虽然基于通配符和正则表达式的搜索提供了非常灵活的搜索,但它们都不能提供一种智能化的选择结果的方法。例如:一个特殊词的搜索将会返回包含这些词的所有行,而不区分包含单个匹配的行和包含多个匹配的行(按照可能是更好地匹配来排列它们)。类似,一个特殊词的搜索将不会找出不包含该词但包含其他相关词的行。

  所有这些限制以及更多的限制都可以用全文本搜索来解决,在使用全文本搜索时,MySQL不需要分别查看每个行,不需要分别分析和处理每个词。MySQL创建指定列中各词的一个索引,搜索可以针对这些词进行。这样,MySQL可以快速有效的决定那些词分配(那些行包含它们),那些词不匹配,它们匹配的频率,等等。

  使用全文本搜索

  为了进行全文本搜索,必须索引被搜索的列,而且要随着数据的改变不断重新索引。在对表列进行适当的设计后,MySQL会自动进行所有的索引和重新索引。

  在索引之后,SELECT可与Match()和Against()一起使用以实际执行搜索。

  启用全文本搜索

  一般在创建表时启用全文本搜索。CREATE TABLE接受FULLTEXT字句,它给出被索引列的一个逗号分隔的列表。

  下面的CREATE语句演示了FULLTEXT字句的使用:

CREATE TABLE productnotes           
(
    note_id int NOT NULL AUTO_INCREMENT,
    prod_id char(10) NOT NULL,
    note_text text NOT NULL,
    note_date datetime NOT NULL,
    PRIMARY KEY(note_id),
    FULLTEXT(note_text)
)ENGINE=MyISAM;

  这些列中有一个名为note_text的列,为了进行全文本搜索,MySQL根据字句FULLTEXT(note_text)的指示对它进行了索引,这里的FULLTEXT索引单个列,如果需要也可以指定多个列。

  在定义之后,MySQL自动维护该索引。在增加、更新或删除行时,索引随之自动更新。

  可以在创建表时指定FULLTEXT,或者在稍后指定。但是不要在导入数据时使用FULLTEXT,因为更新索引需要时间,虽然不是很多,但毕竟要花时间。如果正在导入数据到一个新表,此时不应该启用FULLTEXT索引。应该首先导入所有数据,然后在修改表,定义FULLTEXT,这要有助于更快地导入数据(而且使索引的总时间小于在导入每行每行时分别进行索引所需的总时间)。

  进行全文本搜索

  在索引之后,使用两个函数Match()和Against()函数执行全文本搜索,其中Match指定被搜索的列,Against指定要使用的搜索表达式。

SELECT note_text
 FROM productnotes
WHERE Match(note_text) Against('rabbit');

  输出:note_text

  此SELECT语句检索单个列note_text。由于WHERE子句,一个全文本搜索被执行。Match(note_text)指示MySQL针对指定的列进行搜索,Against('rabbit')指定词rabbit作为搜索文本)。由于有两行包含词rabbit,这两个行被返回。

  注意:使用完整的Match()说明,传递给Match()的值必须与FULLTEXT()定义中的相同。如果指定多个列,则必须列出它们(而且次序必须正确)。

  搜索不区分大小写,除非使用BINARY方式,否则全文本搜索不区分大小写。

  事实是刚才的搜索可以简单的用LIKE子句完成,如下所示:

SELECT note_text
FROM productnotes
WHERE note_text LIKE '%rabbit%';

  检索结果相同,但是次序不同!(虽然并不是总是出现这种情况);

  已知上述两条SELECT语句都不包含ORDER BY子句。后者(LIKE)以不特别有用的顺序返回数据。前者(使用全文本搜索)返回以文本匹配的良好程度排序的数据。两个行都包含词rabbit,但包含词rabbit作为第3个词的行的等级比作为第20个词的行高。这很重要,全文本搜索的一个重要部分就是对结果进行排序,具有较高等级的行先返回(因为这些可能是你真正想要的行)。

  如何查看等级呢?如下所示:

SELECT note_text,Match(note_text) Against('rabbit') AS rank
FROM productnotes

  这里,在SELECT而不是WHERE子句中石油Match()和Against()。这使所有行都被返回(因为没有WHERE子句)。Match()和Against()用来简历一个计算列(别名为rank),此列包含全文本搜索计算出的等级值。等级由MySQL根据行的数目计算出来。正如缩减,不包含rabbit的行等级为0。确实包含rabbit的两行都有一个等级值,文本中词靠前的行等级比词靠后的行的等级值高。

  这个例子有助于说明全文本搜索如何排除行(排除那些等级为0的行),如何排序结果(按等级以降序排序)

  排序多个搜索项:如果指定多个搜索项,则包含多数匹配词的那些航将具有比包含较少词(或仅有一个匹配)的哪些行高的等级值。

  正如所见,全文本搜索提供了简单的LIKE搜索所不能提供的功能。而且,由于数据时索引的,全文本搜索还相当快

  使用查询扩展

  查询扩展是用来设法放宽所返回的全文本搜索结果的范围。在使用查询扩展时,MySQL对数据和索引进行两边扫描来完成搜索。

  1.首先,进行一个基本的全文本搜索,找出与搜索条件匹配的所有行。

  2.其次,MySQL检查这些匹配行并选择所有有用的词。(什么是有用?无用?

  3.再其次,MySQL再次进行全文本搜索,这次不仅使用原来的条件,而且还使用有用的词。

  利用查询扩展,能找出所有可能相关的结果,即时它们并不精确包含所有查找的词。

  查询扩展功能是MySQL4.1.1中引入的。因此不能用于之前的版本。

SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('anvils' WITH QUERY EXPANSION)

  布尔文本搜索

  MySQL支持全文本搜索的另一种形式:称为布尔方式(boolean mode)。以布尔方式,可以提供关于如下内容的细节:

  1.要匹配的词;

  2.要排斥的词(如果某行包含这个词,则不反含该行,即时它包含其他指定的词也是如此);

  3:排列提示:(指定某些词比其它词更重要,更重要的词的等级高);

  4:表达式分组;

  5:另外一些内容;

  布尔文本搜索即使没有FULLTEXT索引也可以使用,布尔方式不同于迄今为止使用的全文本搜索搜索语法的地方在于,即使没有定义FULLTEXT索引,也可以使用它。但这是一种非常缓慢的操作(其性能将随着数据量的添加而降低)。

  为演示IN BOOLEAN MODE的作用,例子如下:

SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('heavy' IN BOOLEAN MODE);

  此全文本搜索检索包含词heavy的所有行(有两行)。其中使用了关键字IN BOOLEAN MODE,但实际上没有指定布尔操作符,因此,其结果与没有指定布尔方式的结果相同。

  为了匹配包含heavy但不包含任意以rope开始的词的所有行,可使用如下查询:

SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('heavy -rope*' IN BOOLEAN MODE);

  我们已经看到可两个全文本搜索布尔操作符 - 和 * ,- 排除一个词,而 * 是截断操作符(可想象为用于词尾的一个通配符)。下列所见为MySQL支持的布尔操作符:

                             全文本布尔操作符

布尔操作符 说明
+ 包含,词必须存在
- 排除,词必须不出现
> 包含,而且增加等级值
< 包含,且减少等级值
() 把词组成子表达式(允许这些子表达式作为一个组被包含、排除、排列等)
~ 取消一个词的牌谑值
* 词尾的通配符
"" 定义一个短语(与单个词的列表不一样,它匹配整个短语以便包含或排除这个短语)

  

 下面举几个例子,说明这些操作符如何使用:

  

SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('+rabbit +bait' IN BOOLEAN MODE);
这个搜索匹配包含词rabbit和bait的行


SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('rabbit bait' IN BOOLEAN MODE);
没有指定操作符,这个搜索匹配包含词rabbit和bait中的至少一个词的行;


SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('“rabbit bait”' IN BOOLEAN MODE);
这个搜索匹配短语rabbit bait,而不是匹配两个词;



SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('>rabbit <carrot' IN BOOLEAN MODE);
这个搜索匹配rabbit 和carrot,增加前者的等级,降低后者的等级;


SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('+safe +(<combination)' IN BOOLEAN MODE);
这个搜索匹配safe 和combination,降低后者的等级;

  全文本搜索数据时,短词被忽略而且从索引中删除。短词定义为那些具有3个或者3个以下字符的词(如果需要,这个数目可以修改)。

  许多词出现的频率高,搜索它们没有用处(返回太多的结果),因此,MySQL定义了一条50%的规则,如果一个词出现在50%以上的行中,则将它作为一个非用词忽略。50%规则不用于IN BOOLEAN MODE

  如果表中的行数小于3行,则全文本搜索将不返回结果(因为每个词或者不出现,或者至少出现在50%的行中)。

  忽略词中的单引号 ' 。

  尽在MyISAM的数据库引擎中支持全文本搜索。