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
時,必須指明RIGHT
,LEFT
關鍵字(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 使用聯結和聯結條件
- 注意聯結類型。一般爲內部聯結,有時需要外部聯結。
- 保證使用正確聯結條件。
- 應該總是提供聯結條件,否則產生笛卡爾積。
- 聯結中可能包含多個表。測試前需要分別測試單個聯結。