《mysql必知必会》笔记(二)

十四:使用子查询

         1:子查询是嵌套在其他查询中的查询

         2:需要列出订购TNT2的所有客户信息,需要下面几步:

a:orderitems表中检索出包含物品TNT2的所有订单号;

b:根据上一步得出的订单号,从orders表中,检索出的所有客户ID

c:根据上一步的客户ID,从customers中检索出客户信息;

         

        它们针对的sql语句分别是:

a:select order_num from orderitems where prod_id = ‘TNT2’; 得到结果

《mysql必知必会》笔记(二)_第1张图片

b:select cust_id from orders where order_num in (20005, 20007); 得到结果如下:


c:select cust_name,cust_contact from customers where cust_id in (10001, 10004); 结果如下:


         上面的三个sql语句,可以合为一句:

select cust_name,  cust_contact from customers where cust_id in

         (select cust_id from  orders  where order_num in

         (select order_num from orderitems where prod_id ='TNT2')

         );

         这就是子查询的例子,为了执行上面的sql语句,mysql实际上执行的就是一开始的那三条语句。子查询总是由内而外处理。

        

         3:另外一个例子,比如要查询customers中,每个客户的订单总数,sql语句如下:

select cust_name, cust_state, (select count(*) from orders where orders.cust_id = customers.cust_id)as orders from customers order by orders; 结果如下:


         本例中的子查询,使用了完全限定名orders.cust_id和customers.cust_id。因为单单使用列名cust_id会有多义性。

         4:注意,使用子查询并不是执行该查询最有效的方法。

 

十五:联接表

         1:sql最强大的功能之一就是,能在数据检索查询的执行中联接表。联接表是利用SQL的select能执行的最重要的操作。

         2:假如有这样的场景,有一个包含产品目录的表,每种类别的物品占一行,包括产品描述,价格,以及供应商的信息。

         如果同一个供应商能生产多种物品,那么如何存储供应商信息?比如供应商名,地址,联系方法等。

         将供应商信息和产品信息分开存储,理由如下:

         a:因为同一供应商生产的每个产品的供应商信息都是相同的,对每个产品重复此信息既浪费时间,又浪费存储空间。

         b:如果供应商信息改变,比如电话或地址变动,则只需改动一次即可;

         c:如果有重复数据,则很难保证每次输入该数据的方式都相同。

        

         关键是,相同数据出现多次,绝不是一个好事,这是关系数据库设计的基础。这个例子中,可建立两个表,一个存储供应商信息,一个存储产品信息。

         vendors表存储供应商信息,每个供应商占一行,具有唯一标示。此标识称为主键

         products表存储产品信息,只存储供应商ID。vendors表的主键,又叫做products表的外键,它将vendors表和products表关联。

         外键是某个表的一列,它包含另一个的主键值,定义了两个表的关系。

 

         3:数据分成多个表存储有好处,但是也有代价,就是:如果数据存储在多个表中,如何用单条select语句检索出数据。这时,就要使用联接。它是一种机制,用来在一条select语句中关联表。联接在运行时关联表中正确的行。

         联接不是物理实体,它在实际的数据库表中不存在,联接有mysql根据需要建立,存在于查询的执行当中

 

         4:select vend_name, prod_name, prod_price from vendors, products where vendors.vend_id = products.vend_id order by vend_name, prod_name; 结果如下:


         5:在一条select语句中联接几个表时,相应的关系是在运行中构造的。在数据库的定义中不存在能指示mysql如何对表进行联接的东西,必须自己做这些事情。

         在联接两个表时,实际上是将第一个表的每一行与第二个表的每一行配对。where子句使得只返回哪些匹配给定条件的行。如果没有where子句,则第一个表中的每行将与第二个表中的每行配对。返回的行数将是第一个表的行数乘以第二个表中的行数。

         所以,应该保证所有联接都有where子句。

 

         6:目前为止使用的联接称为等值联接,它基于两个表的相等测试,也称为内部联接。也可以使用另外一种语法:

         select vend_name, prod_name, prod_price from vendors INNER JOIN products on vendors.vend_id = products.vend_id;

         ANSI SQL规范首选inner join语法。

        

         7:一条select语句中,可以联接的表的数目没有限制,比如:

         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的物品信息:

《mysql必知必会》笔记(二)_第2张图片 

         8:注意,运行时进行关联多个表,是非常耗费资源的

         9:上一章的例子中,返回订购TNT2产品的客户信息,可以用联接处理:

         select cust_name, cust_contact from customers, orders, orderitems where

customers.cust_id= orders.cust_id and

orders.order_num= orderitems.order_num and

orderitems.prod_id= 'TNT2';  结果如下:

《mysql必知必会》笔记(二)_第3张图片 

十六:创建高级联接

         1:sql允许给表起别名,这样可以:缩短SQL语句;允许在单条select语句中多次使用相同的表。比如:

         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

andoi.prod_id = ‘TNT2’;

 

         表别名不仅可用于where子句,还可以用于select列表,order by子句等。

 

         2:自联结的例子:比如在products表中,查找生产‘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’;

         这个例子必须使用表别名。可以想象成两个完全相同的表进行联接的情况。自联结的方式应该会比使用子查询更快一些。

 

         3:外部联接

         联接是将一个表中的行与另一个表中的行相关联,将满足条件的行返回。但有时会需要包含没有关联行的那些行。比如对每个客户下了多少订单进行计数,包括未下订单的客户。这就是外联结。

         比如,检索客户及其订单,使用内联接是:

         select customers.cust_id, orders.order_num from customers inner join orders on

customers.cust_id= orders.cust_id;  得到结果如下:

《mysql必知必会》笔记(二)_第4张图片

         使用外联结:

         select customers.cust_id, orders.order_num from customers left outer join orders on

customers.cust_id = order.cust_id;  得到结果如下:


         可以明显的看出外联结和内联接的不同。使用outer join来指明外联结,外联结还必须指明rightleft,指明需要包含所有行的表。left指明outer join左边的表,right指明outer join右边的表。使用right的例子是:

         select customers.cust_id, orders.order_num from customers right outer join orders on

orders.cust_id = customers.cust_id;

 

         4:如果要检索所有客户以及每个客户所下的订单数,可以如下:

         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;  结果如下:

 

十七:组合查询

         1:mysql允许执行多个查询,并将结果作为单个查询结果集返回。这些组合查询称为union或组合查询。

         2:使用union操作符来组合数条sql查询,比如需要找出价格小于等于5的所有物品的一个列表,而且还需包括供应商1001和1002生产的所有物品,可以使用如下的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); 结果如下:

《mysql必知必会》笔记(二)_第5张图片

         上面使用union的sql语句,也可以使用多条where子句完成:

         select vend_id, prod_id, prod_price from products where prod_price <= 5 or vend_idin (1001, 1002).

 

         3:union的每个查询必须包含相同的列、表达式或聚集函数。

         4: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); 结果如下:

《mysql必知必会》笔记(二)_第6张图片

 

         5:再用union组合查询时,只能使用一条order by子句,它必须出现在最后一条select语句之后。它会排序所有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; 结果如下:

《mysql必知必会》笔记(二)_第7张图片

十八:全文本搜索

         1:mysql支持几种基本的数据库引擎并非所有的引擎支持全文本搜索,比如引擎myisaminnodb,只有myisam引擎支持全文本搜索。全文本搜索比like 和正则表达式具有更强的控制能力。

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

         3:一般在创建表时启用全文本搜索。create table语句接受fulltext子句,他给出被索引列的一个逗号分隔的列表。比如:

create table productnotes

(

  note_id          int                      NOT NULL       AUTO INCREMENT,

  prod_id          char(10)            NOT NULL,

  note_date      datetime            NOT NULL,

  note_text       text                    NULL,     

  primary key(note_id),

  fulltext(note_text)

)

         为了进行全文本搜索,mysql根据子句fulltext(note_text)的指示,对它进行索引。这里fulltext索引单个列,如果需要也可以指定多个列。

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

 

         4:在索引之后,使用两个函数matchagainst进行全文本搜索,其中match指定被搜索的列,against指定要使用的搜索表达式。 比如:

         select note_text from productnotes where match(note_text) against(‘rabbit’);

《mysql必知必会》笔记(二)_第8张图片

         match(note_text)指定mysql针对指定的列进行搜索, against(‘rabbit’)指定词rabbit作为搜索文本。传递给match的值必须与fulltext定义中的相同。如果指定多个列,则必须列出它们,而且次序必须正确。

 

         5:全文本搜索的时候,返回的顺序是按照匹配的良好程度进行排序的数据。尽管两个行都包含rabbit,但是包含词rabbit作为第3个词的行要比作为第20个词的行高。全文本搜索的一个重要部分就是对结果配需,具有较高等级的行先返回。比如:

         select note_text match(note_text) against(‘rabbit’) as rank from productnotes; 其中,每个行的rank值如下:

《mysql必知必会》笔记(二)_第9张图片

         rank列包含全文本搜索计算出的等级值。不包含rabbit行等级为0,确实包含rabbit的两个行每行都有一个等级值。

 

         6:查询扩展用来设法放宽所返回的全文本搜索结果的范围,比如想找出包含anvils的行,还想找出与anvils有关的所有其他行,即使它们不包含anvils在使用查询扩展时,mysql对数据和索引进行两遍扫描来完成搜索:

         a:进行一个基本的全文本搜索,搜索出匹配的所有行;

         b:mysql检查这些匹配行并选择所有有用的词;

         c:mysql再次进行全文本搜索,这次不仅使用原来的条件,而且还使用所有有用的词。

         比如:select note_text from productnotes where match(note_text) against(‘anvils’ with query expansion); 返回结果如下:

《mysql必知必会》笔记(二)_第10张图片

         可见,不仅包含anvils的第一行返回了,还返回了与anvils无关的行。

 

         7:mysql支持全文本搜索的另一种形式:布尔方式。布尔方式可以提供:要匹配的词;要排斥的词;排列提示;表达式分组;另外一些内容。

         即使没有定义fulltext索引,也可以使用布尔方式,但是比较慢。

 

         8:比如为了匹配包含heavy但不包含任意以rope开始的词的行,可以:

         select note_text from productnotes where match(note_text) against(‘heavy –rope*’ inboolean mode);其他布尔方式,查阅其他资料,不再赘述。

 

你可能感兴趣的:(《mysql必知必会》笔记(二))