目錄匯總:SQL 零基礎入門教程
迄今為止,我們使用的只是內聯結或等值聯結的簡單聯結。現在來看三種其他聯結:自聯結(self-join)、自然聯結(natural join)和外聯結 (outer join)。
一、自聯結
如 使用表別名 所述,使用表別名的一個主要原因是能在一條 SELECT 語句中不止一次引用相同的表。下面舉一個例子。
假如要給與 Jim Jones 同一公司的所有顧客發送一封信件。這個查詢要求首先找出 Jim Jones 工作的公司,然后找出在該公司工作的顧客。下面是解決此問題的一種方法:
輸入▼
SELECT cust_id, cust_name, cust_contact
FROM Customers
WHERE cust_name = (SELECT cust_name
FROM Customers
WHERE cust_contact = 'Jim Jones');
輸出▼
cust_id cust_name cust_contact
-------- -------------- --------------
1000000003 Fun4All Jim Jones
1000000004 Fun4All Denise L. Stephens
分析▼
這是第一種解決方案,使用了子查詢。內部的 SELECT 語句 做了一個簡單檢索,返回 Jim Jones 工作公司的 cust_name。該名字用於外部查詢的 WHERE 子句 中,以檢索出為該公司工作的所有雇員(子查詢 中講授了子查詢,更多信息請參閱該部分)。
現在來看使用聯結的相同查詢:
輸入▼
SELECT c1.cust_id, c1.cust_name, c1.cust_contact
FROM Customers AS c1, Customers AS c2
WHERE c1.cust_name = c2.cust_name
AND c2.cust_contact = 'Jim Jones';
輸出▼
cust_id cust_name cust_contact
------- ----------- --------------
1000000003 Fun4All Jim Jones
1000000004 Fun4All Denise L. Stephens
提示:Oracle 中沒有
ASOracle 用戶應該記住去掉
AS。
分析▼
此查詢中需要的兩個表實際上是相同的表,因此 Customers 表在 FROM 子句中出現了兩次。雖然這是完全合法的,但對 Customers 的引用具有歧義性,因為 DBMS 不知道你引用的是哪個 Customers 表。
解決此問題,需要使用表別名。Customers 第一次出現用了別名 c1,第二次出現用了別名 c2。現在可以將這些別名用作表名。例如,SELECT 語句使用 c1 前綴明確給出所需列的全名。如果不這樣,DBMS 將返回錯誤,因為名為 cust_id、cust_name、cust_contact 的列各有兩個。DBMS 不知道想要的是哪一列(即使它們其實是同一列)。WHERE 首先聯結兩個表,然后按第二個表中的 cust_contact 過濾數據,返回所需的數據。
提示:用自聯結而不用子查詢
自聯結通常作為外部語句,用來替代從相同表中檢索數據的使用子查詢語句。雖然最終的結果是相同的,但許多 DBMS 處理聯結遠比處理子查詢快得多。應該試一下兩種方法,以確定哪一種的性能更好。
二、自然聯結
無論何時對表進行聯結,應該至少有一列不止出現在一個表中(被聯結的列)。標准的聯結(聯結 中介紹的內聯結)返回所有數據,相同的列甚至多次出現。自然聯結排除多次出現,使每一列只返回一次。
怎樣完成這項工作呢?答案是,系統不完成這項工作,由你自己完成它。自然聯結要求你只能選擇那些唯一的列,一般通過對一個表使用通配符(SELECT *),而對其他表的列使用明確的子集來完成。下面舉一個例子:
輸入▼
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 = 'RGAN01';
提示:Oracle 中沒有
ASOracle 用戶應該記住去掉
AS。
分析▼
在這個例子中,通配符只對第一個表使用。所有其他列明確列出,所以沒有重復的列被檢索出來。
事實上,我們迄今為止建立的每個內聯結都是自然聯結,很可能永遠都不會用到不是自然聯結的內聯結。
三、外聯結
許多聯結將一個表中的行與另一個表中的行相關聯,但有時候需要包含沒有關聯行的那些行。例如,可能需要使用聯結完成以下工作:
- 對每個顧客下的訂單進行計數,包括那些至今尚未下訂單的顧客;
- 列出所有產品以及訂購數量,包括沒有人訂購的產品;
- 計算平均銷售規模,包括那些至今尚未下訂單的顧客。
在上述例子中,聯結包含了那些在相關表中沒有關聯行的行。這種聯結稱為外聯結。
注意:語法差別
需要注意,用來創建外聯結的語法在不同的 SQL 實現中可能稍有不同。下面段落中描述的各種語法形式覆蓋了大多數實現,在繼續學習之前請參閱你使用的 DBMS 文檔,以確定其語法。
下面的 SELECT 語句給出了一個簡單的內聯結。它檢索所有顧客及其訂單:
輸入▼
SELECT Customers.cust_id, Orders.order_num
FROM Customers
INNER JOIN Orders ON Customers.cust_id = Orders.cust_id;
外聯結語法類似。要檢索包括沒有訂單顧客在內的所有顧客,可如下進行:
輸入▼
SELECT Customers.cust_id, Orders.order_num
FROM Customers
LEFT OUTER JOIN Orders ON Customers.cust_id = Orders.cust_id;
輸出▼
cust_id order_num
---------- ---------
1000000001 20005
1000000001 20009
1000000002 NULL
1000000003 20006
1000000004 20007
1000000005 20008
分析▼
類似 聯結 提到的內聯結,這條 SELECT 語句使用了關鍵字 OUTER JOIN 來指定聯結類型(而不是在 WHERE 子句中指定)。但是,與內聯結關聯兩個表中的行不同的是,外聯結還包括沒有關聯行的行。在使用 OUTER JOIN 語法時,必須使用 RIGHT 或 LEFT 關鍵字指定包括其所有行的表(RIGHT 指出的是 OUTER JOIN 右邊的表,而 LEFT 指出的是 OUTER JOIN 左邊的表)。上面的例子使用 LEFT OUTER JOIN 從 FROM 子句左邊的表(Customers 表)中選擇所有行。為了從右邊的表中選擇所有行,需要使用 RIGHT OUTER JOIN,如下例所示:
輸入▼
SELECT Customers.cust_id, Orders.order_num
FROM Customers
RIGHT OUTER JOIN Orders ON Customers.cust_id = Orders.cust_id;
注意:SQLite 外聯結
SQLite 支持
LEFT OUTER JOIN,但不支持RIGHT OUTER JOIN。幸好,如果你確實需要在 SQLite 中使用RIGHT OUTER JOIN,有一種更簡單的辦法,這將在下面的提示中介紹。
提示:外聯結的類型
要記住,總是有兩種基本的外聯結形式:左外聯結和右外聯結。它們之間的唯一差別是所關聯的表的順序。換句話說,調整
FROM或WHERE子句中表的順序,左外聯結可以轉換為右外聯結。因此,這兩種外聯結可以互換使用,哪個方便就用哪個。
還存在另一種外聯結,就是全外聯結(full outer join),它檢索兩個表中的所有行並關聯那些可以關聯的行。與左外聯結或右外聯結包含一個表的不關聯的行不同,全外聯結包含兩個表的不關聯的行。全外聯結的語法如下:
輸入▼
SELECT Customers.cust_id, Orders.order_num
FROM Customers
FULL OUTER JOIN Orders ON Customers.cust_id = Orders.cust_id;
注意:
FULL OUTER JOIN的支持MariaDB、MySQL 和 SQLite 不支持
FULL OUTER JOIN語法。
請參閱
(完)
