创建高级联结

Posted 霖行

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了创建高级联结相关的知识,希望对你有一定的参考价值。

创建高级联结

本章将讲解另外一些联结类型(包括它们的含义和使用方法),介绍如何对被联结的表使用表别名和聚集函数。

使用表别名

第十章(MySQL必知必会——第十章创建计算字段)介绍了如何使用别名引用表列:

mysql> SELECT Concat(RTrim(vend_name), ' (', RTrim(vend_country), ')') AS vend_title
    -> FROM vendors
    -> ORDER BY vend_name;
+-------------------------+
| vend_title              |
+-------------------------+
| ACME (USA)              |
| Anvils R Us (USA)       |
| Furball Inc. (USA)      |
| Jet Set (England)       |
| Jouets Et Ours (France) |
| LT Supplies (USA)       |
+-------------------------+
6 rows in set (0.00 sec)

别名除了用于表列和计算字段外,SQL还允许给表名取别名,优点:

  • 缩短SQL语句。
  • 允许在单条SELECT语句中多次使用同一张表。

对前一章(MySQL必知必会——第十五章联结表)的例子使用表别名:

mysql> 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';
+----------------+--------------+
| cust_name      | cust_contact |
+----------------+--------------+
| Coyote Inc.    | Y Lee        |
| Yosemite Place | Y Sam        |
+----------------+--------------+
2 rows in set (0.03 sec)

FROM子句中的三个表全部都具有表别名。这使得能省写而不是写全名。表别名不仅能用于WHERE子句,还可以用于SELECT的列表,ORDER BY子句及其他语句部分。

表别名只在查询执行中使用,不返回到客户机。


使用不同类型的联结

到目前我们使用的都是称为内部联结或等值联结(equijoin)的简单联结。还其他的三种联结,分别为自联结、自然联结和外部联结。

自联结

前面所说,别名的一个优点是允许在单条SELECT语句中多次使用同一张表。

假如发现某件物品(如ID为DTNTR)存在问题,因此想知道该物品生产商的其他物品是否同样存在问题。下面是一种查找该物品生产商的所有物品的方法:

mysql> SELECT prod_id, prod_name
    -> FROM products
    -> WHERE vend_id = (SELECT vend_id
    ->                  FROM products
    ->                  WHERE prod_id = 'DTNTR');
+---------+----------------+
| prod_id | prod_name      |
+---------+----------------+
| DTNTR   | Detonator      |
| FB      | Bird seed      |
| FC      | Carrots        |
| SAFE    | Safe           |
| SLING   | Sling          |
| TNT1    | TNT (1 stick)  |
| TNT2    | TNT (5 sticks) |
+---------+----------------+
7 rows in set (0.01 sec)

这是第一种方法,使用子查询(MySQL必知必会——第十四章使用子查询)。首先查出供应商的ID,再根据供应商的ID查出该供应商的物品。

还有一种解决方法是使用联结:

mysql> 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';
+---------+----------------+
| prod_id | prod_name      |
+---------+----------------+
| DTNTR   | Detonator      |
| FB      | Bird seed      |
| FC      | Carrots        |
| SAFE    | Safe           |
| SLING   | Sling          |
| TNT1    | TNT (1 stick)  |
| TNT2    | TNT (5 sticks) |
+---------+----------------+
7 rows in set (0.00 sec)

此查询中的两个表是同一个表。但因为有二义性,所以我们要使用表别名才能正常使用两张表。

  • 用自联结而不用子查询 自联结通常作为外部语句用来替代从相同表中检索数据时使用的子查询语句。虽然结果相同,但有时联结的速度比子查询快得多。

自然联结

对表进行联结,应该保证至少有一个列出现在不止一个表中(被联结的列)。标准的联结返回所有数据,甚至相同的列多次出现。自然联结排除多次出现,使每个列只返回一次

但,系统不完成这项工作,这由你自己完成。自然联结一般通过对表使用通配符(SELECT *) ,对所有其他表的列使用明确的子集完成。

mysql> SELECT c.*, o.order_num, o.order_date, oi.prod_id, oi.quantity, oi.item_price
    -> 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 = 'FB';
+---------+-------------+----------------+-----------+------------+----------+--------------+--------------+-----------------+-----------+---------------------+---------+----------+------------+
| cust_id | cust_name   | cust_address   | cust_city | cust_state | cust_zip | cust_country | cust_contact | cust_email      | order_num | order_date          | prod_id | quantity | item_price |
+---------+-------------+----------------+-----------+------------+----------+--------------+--------------+-----------------+-----------+---------------------+---------+----------+------------+
|   10001 | Coyote Inc. | 200 Maple Lane | Detroit   | MI         | 44444    | USA          | Y Lee        | ylee@coyote.com |     20005 | 2005-09-01 00:00:00 | FB      |        1 |      10.00 |
|   10001 | Coyote Inc. | 200 Maple Lane | Detroit   | MI         | 44444    | USA          | Y Lee        | ylee@coyote.com |     20009 | 2005-10-08 00:00:00 | FB      |        1 |      10.00 |
+---------+-------------+----------------+-----------+------------+----------+--------------+--------------+-----------------+-----------+---------------------+---------+----------+------------+
2 rows in set (0.01 sec)

这个例子中,没有重复的列。

目前我们建立的联结都是自然联结,以后也可能都碰不到非自然联结。

外部联结

许多联结将一张表的行与另一张表的行相关联。但有时我们需要没有关联的行。例如:

  • 对每个客户下了多少订单进行计数,包括没下订单的客户。
  • 列出所有产品以及订购数量,包括没人订购的产品。
  • 计算平均销售规模,包括未下订单的客户。

联结包含在相关表中没有关联行的行,称为外部联结。

检索所有订单和客户的内部联结:

mysql> SELECT customers.cust_id, orders.order_num
    -> FROM customers INNER JOIN orders
    -> ON customers.cust_id = orders.cust_id;
+---------+-----------+
| cust_id | order_num |
+---------+-----------+
|   10001 |     20005 |
|   10001 |     20009 |
|   10003 |     20006 |
|   10004 |     20007 |
|   10005 |     20008 |
+---------+-----------+
5 rows in set (0.01 sec)

使用外部联结,检索所有客户,包括未下订单的客户:

mysql> SELECT customers.cust_id, orders.order_num
    -> FROM customers LEFT OUTER JOIN orders
    -> ON customers.cust_id = orders.cust_id;
+---------+-----------+
| cust_id | order_num |
+---------+-----------+
|   10001 |     20005 |
|   10001 |     20009 |
|   10002 |      NULL |
|   10003 |     20006 |
|   10004 |     20007 |
|   10005 |     20008 |
+---------+-----------+
6 rows in set (0.00 sec)

此SELECT语句使用OUTER JOIN关键字来指定联结的类型,而非WHERE中指定。与内部联结不同的是,外部联结包含了客户10002,而客户10002并未与任何订单相关联。在使用OUTER JOIN语法时,需要使用LEFT或RIGHT指定需要包含所有行的表,LEFT指定关键字左边的表,RIGHT指定关键字右边的表。

选定右边表的所有行:

mysql> SELECT customers.cust_id, orders.order_num
    -> FROM customers RIGHT OUTER JOIN orders
    -> ON customers.cust_id = orders.cust_id;
+---------+-----------+
| cust_id | order_num |
+---------+-----------+
|   10001 |     20005 |
|   10001 |     20009 |
|   10003 |     20006 |
|   10004 |     20007 |
|   10005 |     20008 |
+---------+-----------+
5 rows in set (0.00 sec)
  • 没有*=操作符 MySQL不支持简化字符*=和=*的使用。
  • 外部联结的类型 存在两种基本的外部联结形式:左外部联结和右外部联结。它们唯一差别是所关联的表顺序不同。左外部联结可通过颠倒表的顺序转换为右外部联结。

使用带聚集函数的联结

聚集函数(MySQL必知必会——第十二章汇总数据)用来汇总数据。虽然目前的例子都是从单张表中汇总数据,但可以在联结中使用汇总函数。

检索所有客户及客户下单数:

mysql> 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;
+----------------+---------+---------+
| cust_name      | cust_id | num_ord |
+----------------+---------+---------+
| Coyote Inc.    |   10001 |       2 |
| Wascals        |   10003 |       1 |
| Yosemite Place |   10004 |       1 |
| E Fudd         |   10005 |       1 |
+----------------+---------+---------+
4 rows in set (0.01 sec)

聚集函数也可以方便地与其他联结一起使用:

mysql> 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;
+----------------+---------+---------+
| cust_name      | cust_id | num_ord |
+----------------+---------+---------+
| Coyote Inc.    |   10001 |       2 |
| Mouse House    |   10002 |       0 |
| Wascals        |   10003 |       1 |
| Yosemite Place |   10004 |       1 |
| E Fudd         |   10005 |       1 |
+----------------+---------+---------+
5 rows in set (0.01 sec)

此例子使用外部联结显示所有的客户,包括没有订单的客户。


使用联结和联结条件

关于联结及其使用的要点:

  • 注意所使用的联结类型。一般使用內部联结,但有时使用外部联结。
  • 保证使用正确的联结条件,否则将返回不正确的数据。
  • 应该总是提供联结条件,否则会得出笛卡儿积。
  • 在一个联结中可以包含多个表,甚至对于每个联结可以采用不同的联结类型。但应该对每个联结进行单独测试,从而方便故障的排除。

以上是关于创建高级联结的主要内容,如果未能解决你的问题,请参考以下文章

第十六章 创建高级联结

MySQL必知应会-第16章-创建高级联结

第十六章:创建高级联结

《MySQL必知必会学习笔记》:高级联结

MySQL必知必会第十六章 创建高级联结

MySQL基础之MySQL必知必会(十六)创建高级联结