1. 联结表

SQL最强大的功能之一就是能够在数据检索查询的执行中联结(join)表。

关系表

主键:每行记录的唯一标识。

外键:外键为某个表中的一列,它包含另一个表的主键值,定义两个表之间的关系。

可伸缩性:能够适应不断增加的工作量而不失败。

 

联结是一种机制,用来在一条SELECT语句中关联表,因此称之为联结。使用特殊的语法,可以联结多个表返回一组输出,联结在运行时关联表中正确的行。

创建联结:规定要联结的所有表以及它们如何关联即可。

  SELECT vend_name, prod_name, prod_price

  FROM vendors, products

  WHERE vendors.vend_id = products.vend_id

  ORDER BY vend_name, prod_name;

完全限定名:在引用的列可能出现二义性时,必须使用完全限定列名。

上面的SQL语句中,WHERE子句很重要,它指定了联结的条件。

在一条SELECT语句中联结几个表时,相应的关系是在运行中构造的。

在联结两个表时,实际上做的是将第一个表中的每一行与第二个表中的每一行配对。WHERE字句作为过滤条件,它只包含那些匹配给定条件(这里是联结条件)的行。没有WHERE子句,第一个表中的每个行将与第二个表中的每个行配对,而不管它们是否可以配对。

笛卡尔积:由没有联结条件的表关系返回的结果为笛卡尔积。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数。

 

内部联结:

基于两个表之间的相等测试。之前用逗号分隔的from中的表是内部联结。

  SELECT vend_name,prod_name,prod_price

  FROM vendors INNER JOIN products

  ON vendors.vend_id = products.vend_id;

使用JOIN的联结语法时,联结条件使用ON而不是WHERE。

 

联结可联结多个表。不过出于性能考虑,会减少联结的表的数量。

 

2. 创建高级联结

使用表别名:

表别名的好处:缩短SQL语句,允许在单条SELECT语句中多次使用相同的表。

AS关键字。

 

自联结:

相同的表进行联结:

  SELECT p1.prod_id,p1.prod_name

  FROM products AS p1, products AS p2

  WHERE p1.vend_id = p2.vend_id

  AND p2.prod_id = 'DTNTR';

此查询中需要的两个表实际上是相同的表。

 

自然联结:

无论何时对表进行联结,应该至少有一个列出现在不止一个表中(被联结的列)。标准的联结(之前介绍的内部联结)返回所有数据,甚至相同的列出现多次。

自然联结排除多次出现,使每个列只返回一次。

这需要自己在SELECT检索的列中指出来。

 

外部联结:

联结包含了那些在相关表中没有关联的行。

  SELECT customers.cust_id,orders.order_num

  FROM customers LEFT OUTER JOIN orders

  ON customers.cust_id = orders.cust_id;

OUTER JOIN指定外部联结。

外部联结必须指定是左联结还是右联结。用LEFT和RIGHT在OUTER JOIN之前指示。

 全外联结:FULL OUTER JOIN

 

使用带聚集函数的联结:

 

使用联结和联结条件:

注意使用的联结类型,一般采用内部联结,但也可用外部联结。

保证使用正确的联结条件,且应该总是提供联结条件,否则会得出笛卡尔积。

在一个联结中可以包含多个表,甚至对于每个联结可以采用不同的联结类型。

 

 

3. 组合查询

多数SQL查询都只包含从一个或多个表中返回数据的单条SELECT语句。MySQL也允许执行多个查询(多条SELECT语句);并将结果作为单个查询结果集返回。这些组合查询通常称为并union或符合查询 compound query。

有两种基本情况,其中需要使用组合查询。

(1)在单个查询中从不同的表返回类似结构的数据;

(2)对单个表执行多个查询,按单个查询返回数据。

 

创建组合查询:

可用UNION操作符来组合数条SQL查询。利用UNION可给出多条SELECT语句,将它们的结果组合成单个结果集。

使用UNION:

在各条SELECT语句之间放上关键字UNION。

  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 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规则:

UNION必须由两条或两条以上的SELECT语句组成,语句之间用关键字UNION分隔。

UNION中的每个查询必须包含相同的列、表达式或聚集函数。不过列不需要以相同的次序出现。结果集的表头类型以最高的SELECT语句为准。

列数据类型必须兼容:类型不必完全相同,但必须是DBMS可以隐含地转换的类型。

 

包含或取消重复的行:

UNION从查询结果集中自动去除了重复的行。这时UNION的默认行为。如果需要可以改变它,使用UNION ALL。

  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不取消重复出现的行。

 

对组合结果进行排序:

在用UNION查询时,只能使用一条ORDER BY子句,他必须出现在最后一条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)

  ORDER BY vend_id,prod_price;

 

 

4. 全文本搜索

MySQL有多种引擎,MyISAM支持全文本搜索,InnoDB不支持全文本搜索。

LIKE关键字利用通配操作符匹配文本(和部分文本),能够查找包含特殊值或部分值的行。

利用通配符和正则表达式进行匹配的行一般不使用索引,所以在数据量较大时性能较差。

 

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

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

 

启用全文本搜索支持:

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

  CERATE TABLE productnotes

  (

    note_id  int  NOT NULL AUTO_INCREMENT,

    prod_id  char(10)  NOT NULL,

    note_data  datetime  NOT NULL,

    note_text  text  NULL,

    PRIMARY KEY(note_id),

    FULLTEXT(note_text)

  )ENGINE=MyISAM;

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

可以在创建表时指定FULLTEXT,或者在稍后指定(在这种情况下所有已有数据必须立即索引)。

不要在导入数据时使用FULLTEXT。

 

进行全文本搜索:

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

  SELECT note_text

  FROM productnotes

  WHERE Match(note_text) Against('rabbit');

上述语句指定了搜索列和对该列的搜索表达式。

传递给Match()的值必须与FULLTEXT()定义中的相同。如果未指定多个列,则必须列出他们(而且次序正确)。Match()匹配的数据根据匹配的友好程度排序,即模式越靠近字符串首部的记录排序越靠前。

全文本搜索不区分大小写。

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

 

使用查询扩展:

  SELECT note_text

  FROM productnotes

WITH QUERY EXPANSION);

 

布尔文本搜索:

要匹配的词;要排斥的词;排列提示;表达式分组;另外一些内容。

即使没有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);

Against()函数的模式字符串中-rope*明确地指示MySQL排除包含rope*的行。

 

全文本布尔操作符:

+  包含,词必须存在

-  排除,词必须不出现

>  包含,而且增加等级值

<  包含,且减少等级值

()  把词组成子表达式(允许这些子表达式作为一个组被包含、排除、排列等)

~  取消一个词的排序值

*  词尾的通配符

" "  定义一个短语(与单个词的列表不一样,它匹配整个短语以便包含或排除这个短语)

 

MySQL有一个内建的非用词列表,这些词在索引全文本数据时总是被忽略。如果需要,可以覆盖这个列表。

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