MySQL聯結表與創建高級聯結表.md

15章 聯結表

15.1 聯結

15.1.1 關係表

外鍵(foreign key):外鍵爲某個表中的一列,包含另一個表的主鍵值,定義了兩個表的關係。
可伸縮性(scale):能夠適應不斷增加的工作量而不失敗。

15.1.2 爲何使用聯結

如果數據存儲在多個表中,想要用單條SELECT語句檢索數據? 使用聯結。

15.2 創建聯結

mysql> SELECT vend_name, prod_name, prod_price FROM vendors, products WHERE vendors.vend_id = products.vend_id ORDER BY vend_name, prod_name;
+-------------+----------------+------------+
| vend_name   | prod_name      | prod_price |
+-------------+----------------+------------+
| ACME        | Bird seed      |      10.00 |
| ACME        | Carrots        |       2.50 |
| ACME        | Detonator      |      13.00 |
| ACME        | Safe           |      50.00 |
| ACME        | Sling          |       4.49 |
| ACME        | TNT (1 stick)  |       2.50 |
| ACME        | TNT (5 sticks) |      10.00 |
| Anvils R Us | .5 ton anvil   |       5.99 |
| Anvils R Us | 1 ton anvil    |       9.99 |
| Anvils R Us | 2 ton anvil    |      14.99 |
| Jet Set     | JetPack 1000   |      35.00 |
| Jet Set     | JetPack 2000   |      55.00 |
| LT Supplies | Fuses          |       3.42 |
| LT Supplies | Oil can        |       8.99 |
+-------------+----------------+------------+

分析:與前面語句檢索區別在於兩個列在其中一個表中,而另外一列(vend_name)在另外一個表中。
FROM語句,列出兩個表,分別是vendors 和 products,這裏需要使用 完全限定列名。
完全限定列名:再次強調,出現二義性時,必須使用。

mysql> SELECT vend_name, prod_price FROM vendors, products ORDER BY vend_name, prod_name;
+----------------+------------+
| vend_name      | prod_price |
+----------------+------------+
| ACME           |       5.99 |
| ACME           |       9.99 |
| ACME           |      14.99 |
| ACME           |      10.00 |
| ACME           |       2.50 |
| ACME           |      13.00 |
| ACME           |       3.42 |
| ACME           |      35.00 |
| ACME           |      55.00 |
| ACME           |       8.99 |
| ACME           |      50.00 |
| ACME           |       4.49 |
| ACME           |       2.50 |
| ACME           |      10.00 |
| Anvils R Us    |       5.99 |
| Anvils R Us    |       9.99 |
| Anvils R Us    |      14.99 |
| Anvils R Us    |      10.00 |
| Anvils R Us    |       2.50 |
| Anvils R Us    |      13.00 |
| Anvils R Us    |       3.42 |
| Anvils R Us    |      35.00 |
| Anvils R Us    |      55.00 |
| Anvils R Us    |       8.99 |
| Anvils R Us    |      50.00 |
| Anvils R Us    |       4.49 |
| Anvils R Us    |       2.50 |
| Anvils R Us    |      10.00 |
| Furball Inc.   |       5.99 |
| Furball Inc.   |       9.99 |
| Furball Inc.   |      14.99 |
| Furball Inc.   |      10.00 |
| Furball Inc.   |       2.50 |
| Furball Inc.   |      13.00 |
| Furball Inc.   |       3.42 |
| Furball Inc.   |      35.00 |
| Furball Inc.   |      55.00 |
| Furball Inc.   |       8.99 |
| Furball Inc.   |      50.00 |
| Furball Inc.   |       4.49 |
| Furball Inc.   |       2.50 |
| Furball Inc.   |      10.00 |
| Jet Set        |       5.99 |
| Jet Set        |       9.99 |
| Jet Set        |      14.99 |
| Jet Set        |      10.00 |
| Jet Set        |       2.50 |
| Jet Set        |      13.00 |
| Jet Set        |       3.42 |
| Jet Set        |      35.00 |
| Jet Set        |      55.00 |
| Jet Set        |       8.99 |
| Jet Set        |      50.00 |
| Jet Set        |       4.49 |
| Jet Set        |       2.50 |
| Jet Set        |      10.00 |
| Jouets Et Ours |       5.99 |
| Jouets Et Ours |       9.99 |
| Jouets Et Ours |      14.99 |
| Jouets Et Ours |      10.00 |
| Jouets Et Ours |       2.50 |
| Jouets Et Ours |      13.00 |
| Jouets Et Ours |       3.42 |
| Jouets Et Ours |      35.00 |
| Jouets Et Ours |      55.00 |
| Jouets Et Ours |       8.99 |
| Jouets Et Ours |      50.00 |
| Jouets Et Ours |       4.49 |
| Jouets Et Ours |       2.50 |
| Jouets Et Ours |      10.00 |
| LT Supplies    |       5.99 |
| LT Supplies    |       9.99 |
| LT Supplies    |      14.99 |
| LT Supplies    |      10.00 |
| LT Supplies    |       2.50 |
| LT Supplies    |      13.00 |
| LT Supplies    |       3.42 |
| LT Supplies    |      35.00 |
| LT Supplies    |      55.00 |
| LT Supplies    |       8.99 |
| LT Supplies    |      50.00 |
| LT Supplies    |       4.49 |
| LT Supplies    |       2.50 |
| LT Supplies    |      10.00 |
+----------------+------------+

15.2.1 WHERE子句的重要性

笛卡爾積(cartesian product):在沒有聯結條件的表關係返回的結果爲笛卡爾積。檢索出的行的數目是將第一個表中的行數* 第二表中的行數。
WHERE子句:應當確保所有聯結都是WHERE子句,否則MySQL將會返回比想象的數據多的多。
叉聯結:有時我們會聽到返回稱爲叉聯結(cross join)的笛卡爾積的聯結類型。

15.2.2 內部聯結

目前所有的聯結都是等值聯結(equijion),此聯結也稱爲內部聯結,修改豫劇來明確指定聯結的類型。

mysql> SELECT vend_name,prod_price FROM vendors INNER JOIN products ON vendors.vend_id = products.vend_id ORDER BY vend_name;
+-------------+------------+
| vend_name   | prod_price |
+-------------+------------+
| ACME        |      13.00 |
| ACME        |      10.00 |
| ACME        |       2.50 |
| ACME        |      50.00 |
| ACME        |       4.49 |
| ACME        |       2.50 |
| ACME        |      10.00 |
| Anvils R Us |       5.99 |
| Anvils R Us |       9.99 |
| Anvils R Us |      14.99 |
| Jet Set     |      35.00 |
| Jet Set     |      55.00 |
| LT Supplies |       3.42 |
| LT Supplies |       8.99 |
+-------------+------------+

分析:FROM子句中,以INNER JOIN指定,聯結條件使用特定的 ON子句而不是WHERE傳出。

15.2.3 聯結多個表

-方式:首先列出所有表,然後定義表之間的關係。

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 = 20005;
+----------------+-------------+------------+----------+
| prod_name      | vend_name   | prod_price | quantity |
+----------------+-------------+------------+----------+
| .5 ton anvil   | Anvils R Us |       5.99 |       10 |
| 1 ton anvil    | Anvils R Us |       9.99 |        3 |
| TNT (5 sticks) | ACME        |      10.00 |        5 |
| Bird seed      | ACME        |      10.00 |        1 |
+----------------+-------------+------------+----------+

分析:顯示編號爲20005的訂單物品。通過三個聯結條件過濾出所需要的信息。
注意:性能問題,聯結表越多,性能下降越厲害。

同樣的14章中的例子可以使用聯結表方式

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 = 'TNT2';
+----------------+--------------+
| cust_name      | cust_contact |
+----------------+--------------+
| Coyote Inc.    | Y Lee        |
| Yosemite Place | Y Sam        |
+----------------+--------------+

分析: 這裏沒有使用嵌套子查詢,而是使用兩個聯結。

總結

聯結表稱得上SQL最重要的特徵,有效使用聯結需要對**關係數據庫**設計有基本瞭解。

16章 創建高級聯結

本章講解另一些聯結類型,介紹對聯結的表使用表別名和聚集函數。

16.1 使用表別名

給表名起別名的優點:
1. 縮短SQL語句
2. 允許單條SELECT語句中多次使用相同的表

與14章中相比,下文中更改了表名稱

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        |
+----------------+--------------+

分析:FROM子句中三個表名全部具有別名。注意,表別名只能在查詢執行中使用,與列名不同,表別名不返回客戶機。

16.2 使用不同類型的聯結

此前一直使用的聯結爲內部聯結或等值聯結(equijoin),現有三種其他形式聯結,*自聯結*、**自然聯結**和**外部聯結**。

16.2.1 自聯結

目的:因很多情況下,在一條SELECT子句中不止一次引用相同表格,爲避免繁瑣,引入自聯結

假如需要通過通過某物品,檢索所有此供應商的物品。需要首先查找供應商→檢索所有物品

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

分析:此方法利用 子查詢(嵌套查詢),缺點:多次輸入vend_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) |
+---------+----------------+

分析:縱使使用同一個表,也需要使用 完全限定。同時使用自聯結,用外部語句替代相同表中檢索數據時使用的子查詢語句。性能上,比子查詢快。

16.2.2 自然聯結

  • 宗旨:無論何時對錶聯結,至少有一個列出現在不止一個表中(被聯結的列)。
  • 特點:自然聯結排除多次出現,每個列只出現一次。
  • 工作方式:只能選擇唯一的列,通常使用通配符(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        | [email protected] |     20005 | 2005-09-01 00:00:00 | FB      |        1 |      10.00 |
|   10001 | Coyote Inc. | 200 Maple Lane | Detroit   | MI         | 44444    | USA          | Y Lee        | [email protected] |     20009 | 2005-10-08 00:00:00 | FB      |        1 |      10.00 |
+---------+-------------+----------------+-----------+------------+----------+--------------+--------------+-----------------+-----------+---------------------+---------+----------+------------+

16.2.3 外部聯結

有時需要包含沒有關聯行進行關聯。例如可能需要使用聯結來完成如下工作:
1. 對每個客戶訂單進行計數,包括至今未下訂單的客戶。(此時訂單表中不包含未下訂單客戶)
2. 列出所有產品及訂購數量,包括沒人訂購的產品。

如下,檢索所有客戶和訂單:

mysql> SELECT customers.cust_id, orders.order_num FROM customers INNER JOIN orders ON customers.cust_id = orders.cust_id;(優先使用此種方式)
mysql> SELECT customers.cust_id, orders.order_num FROM customers, orders WHERE customers..cust_id = orders.cust_id;
+---------+-----------+
| cust_id | order_num |
+---------+-----------+
|   10001 |     20005 |
|   10001 |     20009 |
|   10003 |     20006 |
|   10004 |     20007 |
|   10005 |     20008 |
+---------+-----------+

爲了檢索沒有訂單的客戶,需要進行如下(外部聯結)操作:

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 |
+---------+-----------+
  • 分析:使用關鍵字OUTER JOIN來指定聯結類型(不是在WHERE中指定)。與INNER JOIN不同地方在於,還包括爲包含的關聯行。
  • 注意:使用OUTER JOIN時,必須指明RIGHTLEFT關鍵字(RIGHT指出,OUTER JOIN右側的表)。customers LEFT OUTER JOIN orders從左側customers表中選擇所有行。

16.3 使用帶聚集函數的聯結

聚集函數用來彙總函數。可以同聯結一起使用。

如要檢索所有客戶及每個客戶所下訂單數,使用COUNT()完成:

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 |
+----------------+---------+---------+
  • 注意:函數COUNT()需要同 GROUP BY 一起使用,進行分組計算。同時聚集函數也可以用在如下外部聯結
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 |
+----------------+---------+---------+

16.4 使用聯結和聯結條件

  1. 注意聯結類型。一般爲內部聯結,有時需要外部聯結。
  2. 保證使用正確聯結條件。
  3. 應該總是提供聯結條件,否則產生笛卡爾積
  4. 聯結中可能包含多個表。測試前需要分別測試單個聯結。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章