使用子查询:
子查询:
SELECT语句是SQL的查询。在前面所看到的所有SELECT语句都是简单查询,即从单个数据库表中检索数据的单条语句。任何SQL语句都是查询。 但此术语一般指SELECT语句。
SQL还允许创建子查询, 即嵌套在其他查询中的查询。
mysql> SELECT order_num -> FROM orderitems -> WHERE prod_id = 'RGAN01'; +-----------+ | order_num | +-----------+ | 20007 | | 20008 | +-----------+ 2 rows in set (0.00 sec) mysql> SELECT cust_id -> FROM orders -> WHERE order_num IN (20007, 20008); +------------+ | cust_id | +------------+ | 1000000004 | | 1000000005 | +------------+ 2 rows in set (0.22 sec) -- 下面的语句是结合上面的两个查询,把第一个查询变为子查询 mysql> SELECT cust_id -> FROM orders -> WHERE order_num IN (SELECT order_num FROM orderitems WHERE prod_id = 'RGAN01'); +------------+ | cust_id | +------------+ | 1000000004 | | 1000000005 | +------------+ 2 rows in set (0.08 sec)
在SELECT语句中, 子查询总是从内向外处理。 在处理上面的SELECT语句时, DBMS实际上执行了两个操作。首先, 它执行下面的查询:
SELECT order_num FROM orderitems WHERE prod_id='RGAN01'此查询返回两个订单号: 20007和20008。 然后, 这两个值以IN操作符要求的逗号分隔的格式传递给外部查询的WHERE子句。 外部查询变成:
SELECT cust_id FROM orders WHERE order_num IN (20007,20008)
可以看到, 输出是正确的, 与前面硬编码WHERE子句所返回的值相同。
在WHERE子句中使用子查询能够编写出功能很强且很灵活的SQL语句。对于能嵌套的子查询的数目没有限制,不过在实际使用时由于性能的限制,不能嵌套太多的子查询。
子查询的SELECT语句只能查询单个列。企图检索多个列将返回错误。
作为计算字段使用子查询:
mysql> SELECT COUNT(*) AS orders -> FROM orders -> WHERE cust_id = '1000000001'; +--------+ | orders | +--------+ | 2 | +--------+ 1 row in set (0.03 sec) mysql> SELECT COUNT(*) AS orders -> FROM customers -> WHERE cust_id = '1000000001'; +--------+ | orders | +--------+ | 1 | +--------+ 1 row in set (0.00 sec) mysql> SELECT cust_id -> FROM orders; +------------+ | cust_id | +------------+ | 1000000001 | | 1000000001 | | 1000000003 | | 1000000004 | | 1000000005 | +------------+ 5 rows in set (0.00 sec) mysql> SELECT cust_id -> FROM customers; +------------+ | cust_id | +------------+ | 1000000001 | | 1000000002 | | 1000000003 | | 1000000004 | | 1000000005 | +------------+ 5 rows in set (0.00 sec) mysql> SELECT COUNT(*) -> FROM orders, customers -> WHERE orders.cust_id = customers.cust_id; +----------+ | COUNT(*) | +----------+ | 5 | +----------+ 1 row in set (0.00 sec) mysql> SELECT cust_name, cust_state, cust_id, (SELECT COUNT(*) FROM orders WHERE orders.cust_id = customers.cust_id) AS orders -> FROM customers -> ORDER BY cust_id; +---------------+------------+------------+--------+ | cust_name | cust_state | cust_id | orders | +---------------+------------+------------+--------+ | Village Toys | MI | 1000000001 | 2 | | Kids Place | OH | 1000000002 | 0 | | Fun4All | IN | 1000000003 | 1 | | Fun4All | AZ | 1000000004 | 1 | | The Toy Store | IL | 1000000005 | 1 | +---------------+------------+------------+--------+ 5 rows in set (0.00 sec) mysql> SELECT cust_name, cust_state, (SELECT COUNT(*) FROM orders WHERE orders.cust_id = customers.cust_id) AS orders -> FROM customers -> ORDER BY cust_name; +---------------+------------+--------+ | cust_name | cust_state | orders | +---------------+------------+--------+ | Fun4All | IN | 1 | | Fun4All | AZ | 1 | | Kids Place | OH | 0 | | The Toy Store | IL | 1 | | Village Toys | MI | 2 | +---------------+------------+--------+ 5 rows in set (0.02 sec)
上面最后的那个SELECT语句对Customers表中每个顾客返回三列:cust_name、cust_state和orders。orders是一个计算字段,它是由圆括号中的子查询建立的。该子查询对检索出的每个顾客执行一次。在此例中,该子查询执行了5次,因为检索出了5个顾客。
子查询中的WHERE子句与前面使用的WHERE子句稍有不同, 因为它使用了完全限定列名, 而不只是列名( cust_id)。它指定表名和列名( Orders.cust_id和Customers.cust_id)。下面的WHERE子句告诉SQL,比较Orders表中的cust_id和当前正从Customers表中检索的cust_id:
WHERE Orders.cust_id = Customers.cust_id
用一个句点分隔表名和列名,这种语法必须在有可能混淆列名时使用。在这个例子中,有两个cust_id列:一个在Customers中,另一个在Orders中。如果不采用完全限定列名, DBMS会认为要对Orders表中的cust_id自身进行比较。因为
SELECT COUNT(*) FROM Orders WHERE cust_id = cust_id总是返回Orders表中订单的总数, 而这个结果不是我们想要的;
如果在SELECT语句中操作多个表, 就应使用完全限定列名来避免歧义。
上述总结:
1) 什么是子查询
2)如何使用子查询
3)子查询常用与WHERE 子句的 IN操作符中,以及用来填充计算列。
联结表:
SQL最强大的功能之一就是能在数据查询的执行中联结表。联结是利用SQL的SELECT能执行的最重要的操作,很好地理解联结及其语法是学习SQL的极为重要的部分。
关系数据库:相同的数据出现多次决不是一件好事,这是关系数据库设计的基础。关系表的设计就是要把信息分解成多个表,一类数据一个表。各表通过某些共同的值互相关联( 所以才叫关系数据库) 。
为什么使用联结:将数据分解为多个表能更有效地存储,更方便地处理,并且可伸缩性更好。但这些好处是有代价的。
比如:如果数据存储在多个表中,怎样用一条SELECT语句就检索出数据呢?
答案是使用联结。简单说,联结是一种机制,用来在一条SELECT语句中关联表,因此称为联结。使用特殊的语法,可以联结多个表返回一组输出,联结在运行时关联表中正确的行。
创建联结:创建联结非常简单, 指定要联结的所有表以及关联它们的方式即可。如下:
mysql> SELECT vend_name, prod_name, prod_price -> FROM vendors, products -- 要联结的两个表 -> WHERE vendors.vend_id = products.vend_id; -- 联结的条件(这里必须要使用完全限定列名) +-----------------+---------------------+------------+ | vend_name | prod_name | prod_price | +-----------------+---------------------+------------+ | Bears R Us | 8 inch teddy bear | 5.99 | | Bears R Us | 12 inch teddy bear | 8.99 | | Bears R Us | 18 inch teddy bear | 11.99 | | Doll House Inc. | Fish bean bag toy | 3.49 | | Doll House Inc. | Bird bean bag toy | 3.49 | | Doll House Inc. | Rabbit bean bag toy | 3.49 | | Doll House Inc. | Raggedy Ann | 4.99 | | Fun and Games | King doll | 9.49 | | Fun and Games | Queen doll | 9.49 | +-----------------+---------------------+------------+ 9 rows in set (1.09 sec) -- 上面的vend_name在表vendors中,而prod_name, prod_price在products表中 -- vend_id在vendors和products表中都有,所以当作联结的条件 -- 下面测试一下将列名混合,不按照给出的表名顺序排放; 可以看出,没有差错 mysql> SELECT prod_price, vend_name, prod_name -> FROM vendors, products -> WHERE vendors.vend_id = products.vend_id; +------------+-----------------+---------------------+ | prod_price | vend_name | prod_name | +------------+-----------------+---------------------+ | 5.99 | Bears R Us | 8 inch teddy bear | | 8.99 | Bears R Us | 12 inch teddy bear | | 11.99 | Bears R Us | 18 inch teddy bear | | 3.49 | Doll House Inc. | Fish bean bag toy | | 3.49 | Doll House Inc. | Bird bean bag toy | | 3.49 | Doll House Inc. | Rabbit bean bag toy | | 4.99 | Doll House Inc. | Raggedy Ann | | 9.49 | Fun and Games | King doll | | 9.49 | Fun and Games | Queen doll | +------------+-----------------+---------------------+ 9 rows in set (0.01 sec)
WHERE子句的重要性:
在一条SELECT语句中联结几个表时,相应的关系是在运行中构造的。在数据库表的定义中没有指示DBMS如何对表进行联结的内容。你必须自己做这件事情。在联结两个表时,实际要做的是将第一个表中的每一行与第二个表中的每一行配对。 WHERE子句作为过滤条件,只包含那些匹配给定条件( 这里是联结条件)的行。没有WHERE子句,第一个表中的每一行将与第二个表中的每一行配对, 而不管它们逻辑上是否能配在一起。
下面是没有WHERE子句所显示的结果:
mysql> SELECT vend_name, prod_name, prod_price -> FROM vendors, products; +-----------------+---------------------+------------+ | vend_name | prod_name | prod_price | +-----------------+---------------------+------------+ | Bear Emporium | Fish bean bag toy | 3.49 | | Bears R Us | Fish bean bag toy | 3.49 | | Doll House Inc. | Fish bean bag toy | 3.49 | | Fun and Games | Fish bean bag toy | 3.49 | | Furball Inc. | Fish bean bag toy | 3.49 | | Jouets et ours | Fish bean bag toy | 3.49 | | Bear Emporium | Bird bean bag toy | 3.49 | | Bears R Us | Bird bean bag toy | 3.49 | | Doll House Inc. | Bird bean bag toy | 3.49 | | Fun and Games | Bird bean bag toy | 3.49 | | Furball Inc. | Bird bean bag toy | 3.49 | | Jouets et ours | Bird bean bag toy | 3.49 | | Bear Emporium | Rabbit bean bag toy | 3.49 | | Bears R Us | Rabbit bean bag toy | 3.49 | | Doll House Inc. | Rabbit bean bag toy | 3.49 | | Fun and Games | Rabbit bean bag toy | 3.49 | | Furball Inc. | Rabbit bean bag toy | 3.49 | | Jouets et ours | Rabbit bean bag toy | 3.49 | | Bear Emporium | 8 inch teddy bear | 5.99 | | Bears R Us | 8 inch teddy bear | 5.99 | | Doll House Inc. | 8 inch teddy bear | 5.99 | | Fun and Games | 8 inch teddy bear | 5.99 | | Furball Inc. | 8 inch teddy bear | 5.99 | | Jouets et ours | 8 inch teddy bear | 5.99 | | Bear Emporium | 12 inch teddy bear | 8.99 | | Bears R Us | 12 inch teddy bear | 8.99 | | Doll House Inc. | 12 inch teddy bear | 8.99 | | Fun and Games | 12 inch teddy bear | 8.99 | | Furball Inc. | 12 inch teddy bear | 8.99 | | Jouets et ours | 12 inch teddy bear | 8.99 | | Bear Emporium | 18 inch teddy bear | 11.99 | | Bears R Us | 18 inch teddy bear | 11.99 | | Doll House Inc. | 18 inch teddy bear | 11.99 | | Fun and Games | 18 inch teddy bear | 11.99 | | Furball Inc. | 18 inch teddy bear | 11.99 | | Jouets et ours | 18 inch teddy bear | 11.99 | | Bear Emporium | Raggedy Ann | 4.99 | | Bears R Us | Raggedy Ann | 4.99 | | Doll House Inc. | Raggedy Ann | 4.99 | | Fun and Games | Raggedy Ann | 4.99 | | Furball Inc. | Raggedy Ann | 4.99 | | Jouets et ours | Raggedy Ann | 4.99 | | Bear Emporium | King doll | 9.49 | | Bears R Us | King doll | 9.49 | | Doll House Inc. | King doll | 9.49 | | Fun and Games | King doll | 9.49 | | Furball Inc. | King doll | 9.49 | | Jouets et ours | King doll | 9.49 | | Bear Emporium | Queen doll | 9.49 | | Bears R Us | Queen doll | 9.49 | | Doll House Inc. | Queen doll | 9.49 | | Fun and Games | Queen doll | 9.49 | | Furball Inc. | Queen doll | 9.49 | | Jouets et ours | Queen doll | 9.49 | +-----------------+---------------------+------------+ 54 rows in set (0.05 sec)
这种没有联结条件的表关系返回的结果为笛卡儿积(这种又称为叉联结)。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数。
因此,要保证所有联结都有WHERE子句,否则DBMS将返回比想要的数据多得多的数据。同理,要保证WHERE子句的正确性。不正确的过滤条件会导致DBMS返回不正确的数据。
目前为止使用的联结称为等值联结,它基于两个表之间的相等测试。这种联结也称为内联结。下面介绍另一种联结方法:
mysql> SELECT vend_name, prod_name, prod_price -> FROM vendors INNER JOIN products ON vendors.vend_id = products.vend_id; +-----------------+---------------------+------------+ | vend_name | prod_name | prod_price | +-----------------+---------------------+------------+ | Bears R Us | 8 inch teddy bear | 5.99 | | Bears R Us | 12 inch teddy bear | 8.99 | | Bears R Us | 18 inch teddy bear | 11.99 | | Doll House Inc. | Fish bean bag toy | 3.49 | | Doll House Inc. | Bird bean bag toy | 3.49 | | Doll House Inc. | Rabbit bean bag toy | 3.49 | | Doll House Inc. | Raggedy Ann | 4.99 | | Fun and Games | King doll | 9.49 | | Fun and Games | Queen doll | 9.49 | +-----------------+---------------------+------------+ 9 rows in set (0.00 sec)
此语句中的SELECT与前面的SELECT语句相同,但FROM子句不同。这里,两个表之间的关系是以INNER JOIN指定的部分FROM子句。在使用这种语法时,联结条件用特定的 ON 子句而不是WHERE子句给出。传递给ON的实际条件与传递给WHERE的相同。具体使用就看你用哪个更顺手了。
下面是联结多个表的情况:
mysql> 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 = 20007; +---------------------+-----------------+------------+----------+ | prod_name | vend_name | prod_price | quantity | +---------------------+-----------------+------------+----------+ | 18 inch teddy bear | Bears R Us | 11.99 | 50 | | Fish bean bag toy | Doll House Inc. | 3.49 | 100 | | Bird bean bag toy | Doll House Inc. | 3.49 | 100 | | Rabbit bean bag toy | Doll House Inc. | 3.49 | 100 | | Raggedy Ann | Doll House Inc. | 4.99 | 50 | +---------------------+-----------------+------------+----------+ 5 rows in set (0.06 sec)
这里的FROM子句列出三个表,WHERE子句定义这两个联结条件,而第三个联结条件用来过滤出订单20007中的物品。不要联结不必要的表。联结的表越多,性能下降越厉害。
联结中表的最大数目虽然SQL本身不限制每个联结约束中表的数目,但实际上许多DBMS都有限制。 根据具体的DBMS来决定。
mysql> 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 = 'RGAN01')); +---------------+--------------------+ | cust_name | cust_contact | +---------------+--------------------+ | Fun4All | Denise L. Stephens | | The Toy Store | Kim Howard | +---------------+--------------------+ 2 rows in set (0.92 sec) mysql> 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 = 'RGAN01'; +---------------+--------------------+ | cust_name | cust_contact | +---------------+--------------------+ | Fun4All | Denise L. Stephens | | The Toy Store | Kim Howard | +---------------+--------------------+ 2 rows in set (0.03 sec)
比较一下上面的两个查询语句,第二个查询中的返回数据需要使用3个表。但在这里,我们没有在嵌套子查询中使用它们,而是使用了两个联结来连接表。这里有三个WHERE子句条件。前两个关联联结中的表,后一个过滤产品RGAN01的数据。
可以看到,执行任一给定的SQL操作一般不止一种方法。很少有绝对正确或绝对错误的方法。性能可能会受操作类型、所使用的DBMS、表中数据量、是否存在索引或键等条件的影响。因此,有必要试验不同的选择机制,找出最适合具体情况的方法。
上述小结:
1)关系数据库设计的一些基础知识
2)等值联结(内联结)