八、组合查询和全文本搜索
1.UNION
如果我们要执行多个查询条件,比如检索price不高于5或者供应商是1001,1002的商品。我们可以使用where...or...语句:
select products.vend_id,prod_id,prod_price
from products
where prod_price<=5
or products.vend_id in (1001,1002);
但是我们还可以使用union将两个select语句合并起来:
mysql> select products.vend_id,prod_id,prod_price from products where prod_price<=5
-> union
-> select products.vend_id,prod_id,prod_price from products where products.vend_id in (1001,1002);
这两个检索语句结果一样。
注意:
- UNION 中的每个查询必须包含相同的列、表达式或聚集函数。
- UNION自动取消包含重复的行。 上述例子中,第一条select返回4个结果,第二条返回5个,但是使用union之后返回8个而不是9个。因为其中有一条既是1002厂商生产的,又是价格不高于5的。
如果我们不想取消这些行,将union改为union all.
若要排序,只能在最后一条select后面跟上order by.它作用于全部语句。
2.全文本搜索
- 并非所有引擎都支持全文本搜索。 两个最常使用的引擎为MyISAM和InnoDB,前者支持全文本搜索,而后者不支持。
- 在索引之后,SELECT可与Match()和Against()一起使用以实际执行搜索。
也可以稍后指定,不在创建的时候指定。
注意在导入数据的时候不要指定fulltext,因为更新索引需要花费时间。
使用例子:
select note_text from productnotes
where Match(note_text) Against('rabbit');
就可以匹配出出现'rabbit'的文本行。使用LIKE照样可以:
select note_text from productnotes
where note_text like '%rabbit%';
我们使用全文本搜索的优点在于:like返回的记录顺序没有特别的用处(或者说是规律)。而使用全文本搜索,我们是按匹配词的等级返回的。
看下面一条检索语句,我们把等级用数值表示出来:
select note_text,match(note_text) against('rabbit') as rank from productnotes \G;
如图所示,rank值为0的就是不包含匹配词'rabbit'的记录。我们返回顺序是优先级从高到底的顺序。
等级值:
- 等级由MySQL根据行中词的数目、唯一词的数目、整个索引中词的总数以及包含该词的行的数目计算出来。
- 文本中词靠前的行的等级值比词靠后的行的等级值高。
3. 查询扩展
除了查找出现匹配词的行之外,我们可能还需要检索出与这个词有关的行,但匹配的词却没有出现在这行中。
如我们要检索与'anvils'匹配的行:
mysql> select note_text from productnotes
-> where match(note_text) against('anvils');
这样我们只返回一条数据,其中包含'anvils'.
使用扩展查询时候:
- 首先,进行一个基本的全文本搜索,找出与搜索条件匹配的所有行。
- 其次,MySQL检查这些匹配行并选择所有有用的词。
- 再其次,MySQL再次进行全文本搜索,这次不仅使用原来的条件,而且还使用所有有用的词。
所以MySQL进行查询扩展的时候进行了两次的全文搜索。
至于是如何选择有用的词,则之后学习。
语法:
mysql> select note_text from productnotes
-> where match(note_text) against('anvils' with query expansion);
这次我们返回了7条记录:
原因如上图。
4. 布尔文本搜索
布尔文本搜索即使没有fulltext索引也可以使用。
布尔方式可以提供以下细节:
-
要匹配的词。
-
要排斥的词(如果某行包含这个词,则不返回该行,即使它包含其他指定的词也是如此)。
-
排列提示(指定某些词比其他词更重要,更重要的词等级更高)。
-
表达式分组。
select note_text from productnotes
-> where match(note_text) against('heavy' in boolean mode);
此条布尔查询和全文本搜索一致。但我们要如果需要排斥一个词呢?mysql> select note_text from productnotes
-> where match(note_text) against('heavy -rope*' in boolean mode);
此条布尔查询就可以返回不以'rope'开头的含有'heavy'的记录。
实例:
5.全文本搜索使用说明。
- 短词被忽略。 在索引全文本数据时,短词被忽略且从索引中排除。短词定义为那些具有3个或3个以下字符的词(如果需要,这个数目可以更改)。
- 内建词被忽略。 MySQL带有一个内建的非用词(stopword)列表,这些词在索引全文本数据时总是被忽略。如果需要,可以覆盖这个列表。
- 50%规则。 许多词出现的频率很高,搜索它们没有用处(返回太多的结果)。因此,MySQL规定了一条50%规则,如果一个词出现在50%以上的行中,则将它作为一个非用词忽略。50%规则不用于 IN BOOLEAN MODE。
- 小于三行不返回。 如果表中的行数少于3行,则全文本搜索不返回结果(因为每个词或者不出现,或者至少出现在50%的行中)。
- 忽略单引号。 忽略词中的单引号。例如, don't 索引为 dont 。
- 需要词分隔符。 不具有词分隔符(包括日语和汉语)的语言不能恰当地返回全文本搜索结果。
- 仅在 MyISAM 数据库引擎中支持全文本搜索。