Chapter 11 ~ 12 使用子查询 和 联结表

使用子查询:

子查询:

    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)等值联结(内联结)


你可能感兴趣的:(Chapter 11 ~ 12 使用子查询 和 联结表)