《MySQL必知必会》15~18章

第十五章~第十八章

第十五章

关系表的设计就是要保证把信息分解成多个表,一类数据一个表,各表通过某些常用的值(即关系设计中的关系互相关联 )

比如我们可以建立两个表,一个存储供应商信息,另一个存储产品信息,vendors表包含所有供应商信息,每个供应商占一行,它们的唯一标识称为主键,如供应商ID,products表只存储产品信息,除了供应商ID不存储其他供应商的信息,因此vendors的主键又叫作products的外键,它将两个表关联,利用供应商ID即可从vendors表中找出相应供应商的信息。

外键:外键为某个表中的一列,它包含另一个表的主键值

优点:

· 供应商信息不重复,节省时间和空间

· 如果供应商信息变动,可以只更新vendors表中的单个记录,相关表中的数据不用改动

· 由于数据无重复,显然数据是一致的,使得数据处理更简单,关系数据库的可伸缩性远好于非关系数据库

可伸缩性:能够适应不断增加的工作量而不失败,设计良好的数据库或应用程序称为可伸缩性好

在一条SELECT语句中关联表,称为联结,使用这种语法可以联结多个表返回一组输出,联结在运行时关联表中正确的行

在使用关系表时,仅在关系列中插入合法的数据非常重要,若在products表中插入拥有非法供应商ID(即没有在vendorsd表中出现)的供应商成产的产品,则这些产品是不可访问的,因为没有关联到某个供应商

创建联结非常简单,规定要联结的所有表以及它们如何关联即可

SELECT vend_name, prod_name, prod_price
FROM vendors, products
WHERE vendors.vend_id = products.vend_id
ORDER BY vend_name, prod_name;

在一条SELECT语句中联结几个表时,相应的关系是在运行中构造的,数据库本身不能指示如何对表进行联结,因此需要WHERE语句,在联结两个表时,你实际上做的是将第一个表中的每一行与第二个表中的每一行配对,通过WHERE子句进行筛选(即联结条件)

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

SELECT vend_name, prod_name, prod_price
FROM vendors, products
ORDER BY vend_name, prod_name;

我们要保证所有联结都有WHERE子句

目前所用的联结为等值联结,基于两个表之间的相等测试,也称内部联结,对于这种联结可以用另一种不同的方式,在这里,两个表的关系用INNER JOIN指定

SELECT vend_name, prod_name, prod_price
FROM vendors INNER JOIN products
ON vendors.vend_id = products.vend_id;

联结多个表

SELECT prod_name, vend_name, prod_price, quantity
FROM orderitems, products, vendors
WHERE products.vend_id = vendors.vend_id
	AND orderitems.prod_id = products.prod_id
	AND order_num = 20005;

此例子显示编号为20005的订单中的物品,根据order_num=20005对应行的prod_id来找到表products表中的物品,在根据对应的vend_id来找到vendors中对应的公司

联结的表越多,性能下降越厉害

回顾之前14章的子查询

SELECT cust_name, cust_contact
FROM customers
WHERE cust_id IN (SELECT cust_id
                  FROM orderts
                  WHERE order_num IN (SELECT order_num
                                      FROM order_items
                                      WHERE prod_id = 'TNT2'));

可以使用联结

SELECT cust_name, cust_contact
FROM customers, orders, orderitems
WHERE customers.cust_id = orders.cust_id
	AND orderitems.order_num = orders.order_num
	AND prod_id = 'TNT2';

WHERE子句前两个关联,后一个过滤

第十六章

别名除了用于列名,计算字段(与列相似)外,。SQL还允许给表名起别名,达到缩短SQL语句,方便多次使用表名的目的

SELECT cust_name, cust_contact
FROM customers AS c, orders AS o, orderitems AS oi
WHERE c.cust_id = o.cust_id
	AND oi.order_num = o.order_num
	AND prod_id = 'TNT2';

表别名不仅能用于WHERE子句,还可以用于SELECT的列表、ORDER BY子句以及其他部分,但要注意表别名只在查询执行中进行,不返回到客户机

之前使用的只是内部联结或等值联结,接下来介绍别的联结

自联结,假如发现某物品(ID为DTNTR)存在问题,因此想知道该商品供应商的其他物品有没有问题,所以要找出这个供应商生产的所有物品,可以使用子查询

SELECT prod_id, prod_name
FROM products
WHERE vend_id = (SELECT vend_id
                FROM products
                WHERE prod_id = 'DTNTR');

使用联结

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';

此查询中的两个表其实是相同的表,但对products表的引用具有二义性。MySQL不知道引用的是products的哪个实例,因此用p1,p2区分

在对表进行联结时,至少有一个或多个列出现在不止一个表中,因此标准的联结可能会返回重复的列,自然联结可以排除这种情况,每个列只返回一次,但需要我们自己完成

SELECT c.*, o.order_num, o.order_date,
	   oi.prod_id, oi.quantity, oi.item_price
FROM customers ASc, orders AS o, orderitems AS oi
WHERE c.cust_id = o.cust_id
	AND oi.order_num = o.order_num
	AND prod_id = 'FB';

对一个表使用通配符,所有其他列明确列出,所以不会重复,事实上我们很可能永远都不会用到不是自然联结的内部联结

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

先使用内部联结检索所有有订单的客户

SELECT customers.cust_id, orders.order_num
FROM customers INNER JOIN orders
  ON customers.cust_id = orders.cust_id;

外部联结检索出所有客户

SELECT customers.cust_id, orders.order_num
FROM customers LEFT OUTER JOIN orders
  ON customers.cust_id = orders.cust_id;

外部联结还包括没有关联行的行,在使用OUTER JOIN时,必须使用LEFT或RIGHT关键字来指定包括其所有行的表,上例中使用LEFT OUTER JOIN从customers表中所有行,若要选择右边表(orders)中的所有行,则使用RIGHT OUTER JOIN,如下例所示(ON子句中等号两边位置可互换,只是一个联结条件)

SELECT customers.cust_id, orders.order_num
FROM customers RIGHT OUTER JOIN orders
  ON orders.cust_id = customers.cust_id;

聚集函数可以与联结一起使用,如要检索所有客户及每个客户所下的订单数,可使用COUNT()函数,按客户分组数据

SELECT customers.cust_name,
	   customers.cust_id,
	   COUNT(orders.order_num) AS num_ord
FROM customers INNER JOIN orders
ON customers.cust_id = orders.cust_id
GROUP BY customers.cust_id;

也可以与外部联结一起使用,没有订单的客户也会显示出来

SELECT customers.cust_name,
	   customers.cust_id,
	   COUNT(orders.order_num) AS num_ord
FROM customers LEFT OUTER JOIN orders
ON customers.cust_id = orders.cust_id
GROUP BY customers.cust_id;

使用联结和联结条件

· 注意联结类型,一般使用内部联结,但外部联结也是有效的

· 使用正确的联结条件,且必须有联结条件,否则会得到笛卡尔积

第十七章

MySQL允许执行多个查询(多条SELECT语句),并将结果作为单个查询结果集返回,这些组合查询通常称为并或复合查询

有两种基本情况需要使用组合查询:

· 在单个查询中从不同的表返回类似结构的数据

· 对单个表执行多个查询,按单个查询返回数据

任何具有多个WHERE子句的SELECT语句都可以作为一个组合查询给出

可用UNION操作符来组合数条SQL查询,将它们的结果组合成单个结果集

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);

这里也可以使用多条WHERE子句来完成相同的任务

SELECT vend_id, prod_id, prod_price
FROM products
WHERE prod_price <= 5
   OR vend_id IN (1001, 1002);

在对于更复杂的过滤条件或从多个表中检索数据,使用UNION会更加简单

UNION规则

· UNION必须由两条或两条以上的SELECT语句组成

· UNION中的每个查询必须包含相同的列、表达式或聚集函数(但各个列不需要以相同的次序列出 )

· 列数据类型必须兼容,类型不必完全相同,但必须是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可以完成WHERE完成不了的工作,如果确实需要所有行(包括重复行)出现,则必须使用UNION ALL而不是WHERE

对组合查询结果可以进行排序,但只能使用一条ORDER BY子句,且必须出现在最后一条SELECT语句之后,MySQL用它来排序所有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;

使用UNION的组合查询不仅仅用于一张表,可以应用于不同的表

第十八章

MyISAM支持全文本搜索而InnoDB不支持

使用LIKE关键字或者正则表达式要求MySQL匹配表中所有行,随着行数增加,会很耗时,且不容易明确匹配什么不匹配什么,例如指定一个词必须匹配,一个词必须不匹配且结果不够智能化,如不会区分单个匹配的行和多个匹配的行(一行中匹配同一个行多次)

这些问题可以用全文本搜索解决,此时MySQL不需要分别查看每个行,也不需要分别分析和处理每个词

为了进行全文本搜索,必须索引被搜索的列,在CREATE TABLE时,MySQL根据子句FULLTEXT(note_text)的指示对该列进行索引,如果需要也可以指定多个列,可以在创建表时指定FULLTEXT,或者稍后,不要在导入数据时使用FULLTEXT

在索引后,使用函数Match(),Against(),Match()指定被搜索的列,Against()指定要使用的搜索表达式

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

若改成’rabbi’,则没有结果

传递给Match()的值必须与FULLTEXT()定义中的相同,若指定多个,则必须列出它们且次序正确,除非使用BINARY方式,否则不会区分大小写

刚才的搜索也可以使用LIKE子句完成

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

都没有使用ORDER BY子句且返回两条结果但全文本以文本匹配的良好程度排序,具有较高等级的行先返回

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

在新版MySQL中rank为保留字,所以改为rank1

在SELECT而不是WHERE使用两个函数使所有行均被返回,rank1列表示每行全文本搜索计算出的等级值

等级由MySQL根据行中词的数目、唯一词的数目等方式计算,不包含’rabbit’的行等级为0,因此若使用WHERE子句等级值为0的排除,并根据等级值降序排列

若指定多个搜索项,包含更多数匹配词的行的等级更高

查询扩展可以用来放宽搜索结果的范围,即该行没有指定单词,但与搜索有关

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

通过对数据和索引进行两遍扫描来完成搜索

先进行一个基本的全文本搜索,找出与上述条件匹配的所有行,上例中搜索出一条,然后MySQL检查匹配的行并选出这些行中有用的词(如customer和recommend),加上这些有用的词再进行一次全文本搜索,结果中包含词anvils的行在最前

布尔文本搜索指定要匹配的词,要排斥的词(即使该行包含指定的词也不返回),排列提示(指定某些词比其他词等级更高更重要),且即使没有定义FULLTEXT索引,也可以使用,但是速度相对较慢

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

SELECT note_text
FROM productnotes
WHERE Match(note_text) 
	  Against('heavy -rope*' 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)
	  Agaisnt('rabbit bait' IN BOOLEAN MODE);

匹配短语rabbit bait而不是分别匹配这两个词

SELECT note_text
FROM productnotes
WHERE Match(note_text)
	  Against('"rabbit bait"' IN BOOLEAN MODE);

二选一,增加前者等级,降低后者等级

SELECT note_text
FROM productnotes
WHERE Match(note_text)
	  Against('>rabbit <carrot' IN BOOLEAN MODE);

匹配两个词,降低后者的等级

SELECT note_text
FROM productnotes
WHERE Match(note_text) 
	  Against('+safe +(<combination)' IN BOOLEAN MODE);

排列而不排序:在布尔方式中,不按等级值降序排序返回的行

关于全文本搜索的注意事项

· 在索引全文本数据时,短词被忽略且从索引中排除,短词定义为3个即以下字符的词

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

· 如果一个词出现在50%以上的行中,则将它作为一个非用词忽略,这个规则不适用于IN BOOLEAN MODE

· 表中行数<=2行,全文本搜索不返回结果,因为50%的规则

· 忽略单引号 如don’t索引为dont

发布了84 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43569916/article/details/104375793
今日推荐