欢迎关注微信公众号:摸鱼汪的杂货铺
多是SQL查询都只包含从一个或多个表中返回数据的单条SELECT语句。MySQL也允许执行多个查询(多条SELECT语句),并将结果作为单个查询结果集返回。这些组合查询通常称为并(union)或符合查询(compound query)。
有两种基本情况使用组合查询:
1.在单个查询中从不同的表返回类似结构的数据;
2.对单个表执行 多个查询,按单个查询返回数据。
可用UNION操作符来组合数条SQL查询。利用UNION,可以给出多条SELECT语句,将他们的结果组合成单个结果集。
UNION的使用很简单。所需要做的只是给出每条SELECT语句,在各条语句之间放上关键字NUION。
举一个例子,假如需要价格小于5所有物品的一个列表,而且还想包括供应商1001和1002生产的所有物品(不考虑价格)。
我们先来看单挑SELECT语句的编写:
SELECT vend_id,prod_id,prod_price FROM products WHERE prod_price <= 5;
SELECT vend_id,prod_id,prod_price FROM products WHERE vend_id IN(1001,1002);
第一条SELECT检索价格不高于5的所有产品,第二条SELECT使用IN找出供应商为1001和1002的所有物品。组合这两条SELECT语句可以按如下进行:
SELECT vend_id,prod_id,prod_price FROM products WHERE prod_price <=5 UNION SELECT vend_id,prod_id,prod_price FROM products WHERE vend_id IN (1001,1002);
这条语句由前面两条SELECT语句组成,语句中用UNION故拿剪子分隔。UNION只是MySQL执行两条SELECT语句,并把输出组合成单个查询结果集。
作为参考这里给出多条WHERE子句而不是使用UNION的相同查询:
SELECT vend_id,prod_id,prod_price FROM products WHERE vend_id IN (1001,1002) OR prod_price <=5;
在这个简单的列子中使用UNION可能比使用WHERE子句更为复杂。但对于更复杂的过滤条件,或这从多个表(而不是单个表)中检索数据的情形,使用UNION可能会使处理更简单。
正如所见,并不是非常容易使用到的,但在进行并时有几条规则需要注意:
1.UNION必须由两条或两条以上的SELECT语句组成,语句之间用关键字UNION分隔(因此,如果组合4条SELECT语句,将要使用3个UNION关键字)。
2.UNION中的每个查询必须包含相同的列、表达式或聚集函数(不过各列不需要以相同的次序列出)。
3.列数据类型必须兼容:类型不必完全相同,但必须是DBMS可以隐含地转换的类型(例如,不同的数值类型或不同的日期类型)
请返回到17.2.1节认证查看所用样例,我们发现第一条SELECT语句返回4条,而第二条SELECT语句返回5行,但使用UNION组合语句的SELECT语句只返回8行而不是9行。
UNION从查询结果集中自动去除了重复的行(换句话说,它的行为与单条SELECT中使用多个WHERE子句条件一样)。因为供应商
生产的一种物品的介个也低于,所以两条SELECT语句都返回改行,在使用UNION时,重复的行被自动取消。
这是UNION默认的行为,但是如果需要,可以改变它,实际上如果想要返回所有匹配的行,可以使用UNION ALL而不是UNION,请看下面的例子:
SELECT vend_id,prod_id,prod_price FROM products WHERE prod_price <=5 UNION ALL SELECT vend_id,prod_id,prod_price FROM products WHERE vend_id IN (1001,1002);
使用UNION ALL,MySQL不取消重复的行。因此这里的例子返回9行,其中一行出现两次。
SELECT语句的输出用ORDER BY子句排序。在用UNION组合查询时,只能使用一条ORDER BY子句。它必须出现在最后一条SELECT语句之后,对于结果集不存在用一种方式排序一部分,而又用另一种方式排序令一部分的情况,因此不允许使用多条ORDER BY子句。
用下面例子排序前面UNION返回的结果:
SELECT vend_id,prod_id,prod_price FROM products WHERE prod_price <=5 UNION ALL SELECT vend_id,prod_id,prod_price FROM products WHERE vend_id IN (1001,1002) ORDER BY vend_id,prod_price;
这条UNION在最后一条SELECT语句后使用了ORDER BY子句。虽然ORDER BY子句似乎是最后一条SELECT语句的组成部分,但实际上MySQL将用它来排序所有SELECT语句返回的结果。
第八节介绍了LIKE关键字,他利用通配符匹配文本(和部分文本)。使用LIKE,能够查找包含特殊值或部分值的行(不管这些值位于列什么位置)。
在第九节中,用基于文本搜索作为正则表达式匹配列值的更进一步介绍。使用正则表达式,可以编写查找所需行的非常负载的匹配模式。
虽然这些搜索机制非常有用,但存在几个重要的限制:
1.性能——通配符和正则表达式匹配通常要求MySQL尝试匹配表中所有行(而且这些搜索极少使用表索引)。因此,由于被搜索行数不断增加,这些搜索可能非常耗时。
2.明确控制——使用通配符和正则表达式。很难(而且并不总是能)明确的控制匹配什么和不匹配什么。
3.智能化结果——虽然基于通配符和正则表达式的搜索提供了非常灵活的搜索,但他们都不能提供一种智能化的选择结果的方法。
为了进行全文本搜索,必须索引被搜索的列,而且要随着数据的改变不断地重新索引。在对表列进行适当设计后,MySQL会自动进行所有的索引和重新索引。
在索引之后SELECT可与Match()和Against()一起使用实际执行搜索。
一般在创建表时启用全文本搜索,CREATE TABLE语句接受FULLTEXT子句,他给出被索引列的一个逗号分隔的列表。下面的CREATE语句演示了FULLTEXT子句的使用:
CREATE TABLE productes( note_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, prod_id CHAR(10) NOT NULL, note_date DATETIME NOT NULL, note_text TEXT NULL FULLTEXT, )
第二十一节会详细介绍CREATE TABLE语句,现在只需要知道用于创建数据库表就行。在这些列中note_text为了进行全文本搜索,MySQL根据子句FULLTEXT(note_text)的指示对它进行索引。这里的FULLTEXT索引单个列,如果需要也可以指定多个列。
在索引之后,使用两个函数Match()和Against()执行全文本搜索,其中Match()指定被搜索的列,Against()指定要使用的搜索表达式。
下面举一个列子:
SELECT note_text FROM productnotes WHERE MATCH(note_text) AGAINST('rabbit')
此SELECT语句检索单个列note_text。由于WHERE子句,一个全文本搜索被执行。MATCH(note_text)指示MySQL针对指定的列进行搜索,AGAINST(‘rabbit’)指定词rabbit作为搜索文本。由于有两行包含词rabbit,这两个行被返回。
事实上这个例子也可以用简单的LIKE子句完成,如下面所示:
SELECT note_text FROM productnotes WHERE note_text LIKE '%rabbit%';
这条SELECT语句同样检索出两行,但次序不同(虽然并不总是出现这种情况)。
上述几条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(因此不被前一个例子中的WHERE子句选择)。确实包含词rabbit的两个行每行都有一个等级值,文本中词靠前的行的等级比词靠后的行的等级值高。
这个例子有助于说明全文本搜索如何排除行(排除那些等级为0的行),如何排序结果(按等级以降序排序)。
查询扩展用来设法放宽所返回全文本搜索结果的范围。考虑下面的情况。你想找出所有提到anvils的注释。只有一个注释包含词anvils,但你还想找出可能与你搜索有关的所有其他行,即使它们不包含词anvils。
这也是查询扩展的一项任务。在使用查询扩展时,MySQL对数据和索引进项两遍扫描来完成搜索。
1.首先,进行一个基本的全文本搜索,找出于搜索体哦阿健匹配的所有行。
2.其次,MySQL检查这个匹配行并选择所有有用的词(我们将会简要的解释MySQL如何断定什么有用,什么没用)
3.再其次,MySQL再次进行全文本搜索,这次不仅使用原来的条件,而且还使用所有有用的词。
利用查询扩展,能找到可能相关的结果,即使它们并不精确包含所查找的词。
扩展查询功能只应用于MySQL4.1.1或更高级的版本
下面举一个例子,首先进行一个简单的全文本搜索,没有查询扩展:
SELECT note_text FROM productnotes WHERE MATCH(note_text) AGAINST('anvils');
只有一行包含词anvils,因此只返回一行
下面是相同的搜索,这次使用查询扩展:
SELECT note_text FROM productnotes WHERE MATCH(note_text) AGAINST('anvils' WITH QUERY EXPANSION);
这次返回了7行。第一行包含词anvils,因此等级最高。第二行与anvils无关,但因为它包含第一行中的两个词(customer和recommend),所以也被检索出来。第三行也包含这两个相同的词,但它们在文本中的位置更靠后且分开的更远,因此在这一行,但等级为第三行。第三行确实也没有设计anvils。
正如所见,查询扩展极大地增加了返回地行数,但这样也增加了你实际上并不想要地行地数目。
MySQL支持全文本搜索地另外一种形式,称为布尔方式(boolean mode)。以布尔方式,可以提供关于如下内容地细节:
1.要匹配的词;
2.要排斥的词(如果某行包含这个词,则不会返回改行,即使它包含其他指定的词也是如此);
3.排列提示(指定某些词比其他词更重要,更重要的此等级更高);
4.表达式分组;
5.另外一些内容。
为演示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);
这次只返回一行。这次仍然匹配词heavy,但-rope*明确地指示MySQL排除包含rope *(任何以rope开始的词,包括ropes)的行,这就是为什么返回上一个例子中的第一行被排除的原因。
表全文本布尔操作符列出支持的所有布尔操作符。
下面举几个例子,说明某些操作符如何使用:
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而不是匹配两个词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和combinnation,降低后者的等级
1.在索引全文本数据时,短词被忽略且从索引中排除。短词定义为那些具有3个或3个以下字符的词(如果需要,这个数目可以更改)
2.MySQL带有一个内建的非用词(stopword)列表,这些词在索引全文本数据时总是被忽略。如果需要,可以覆盖这个列表
3.许多词出现的频率很高,搜索它们没有用处(返回太多结果)。因此,MySQL规定了一条50%规则不用于IN BOOLEAN MODE。
4.如果表中的行数少于3行,则全文本搜索不反悔结果(因为每个词或者不出现,或者至少出现在50%的行中)。
5.忽略词中的单引号。例如don’t索引为dont。
1.在索引全文本数据时,短词被忽略且从索引中排除。短词定义为那些具有3个或3个以下字符的词(如果需要,这个数目可以更改)
2.MySQL带有一个内建的非用词(stopword)列表,这些词在索引全文本数据时总是被忽略。如果需要,可以覆盖这个列表
3.许多词出现的频率很高,搜索它们没有用处(返回太多结果)。因此,MySQL规定了一条50%规则不用于IN BOOLEAN MODE。
4.如果表中的行数少于3行,则全文本搜索不反悔结果(因为每个词或者不出现,或者至少出现在50%的行中)。
5.忽略词中的单引号。例如don’t索引为dont。
6.不具有此分隔符(包括日期和汉语)的语言不能恰当的返回全文本搜索结果。