外部聯結(Outer Join)和笛卡兒積(Cartesian Product)
在這個關於「常見的 SQL 查詢錯誤」的系列中,我們一直在探索看似直觀的 SQL 查詢建構方法如何導致反模式,從而導致錯誤的結果和/或效能降低。上週,我們暫停了這個系列,討論了 SQL 中的述詞。在本期文章中,我們將學習它們的位置如何對查詢執行產生負面影響,尤其是在外部聯結中。
什麼是外部聯結?
在連結相關資料表和檢視時,可使用四種基本的聯結類型:內部聯結、左方聯結、右方聯結與外部聯結。內部聯結不會傳回任何一個資料表中在另一個資料表中不相符的列。外部聯結可以傳回一個或兩個資料表中不相符的列。而最後三種聯結類型都是外部聯結的一種,其中:
- LEFT JOIN 只傳回左資料表中不相符的列。
- RIGHT JOIN 只傳回右資料表中不相符的列。
- FULL OUTER JOIN 傳回兩個資料表中不相符的列。
外部聯結如何出錯
雖然外部聯結在資料庫從業員中肯定佔有一席之地,但開發人員傾向於使用它們,即使在不需要它們的情况下也是如此。此外,外部聯結查詢可能會產生完全不同的結果,具體取決於你構建它的方式以及你在查詢中放置謂詞的位置。為了說明這一點,讓我們看看一個範例。
我們想擷取所有客戶的清單(無論他們是否下過任何訂單),以及自 2005 年 6 月初以來他們下的訂單總數。為此,我們將使用外部聯結來連結 customers 和 orders 資料表,如下所示:
SELECT C.customerName, count(O.customerNumber) AS 2005_orders FROM customers AS C LEFT OUTER JOIN orders AS O ON C.customerNumber = O.customerNumber WHERE O.orderDate >= '2005-05-01' GROUP BY C.customerName ORDER BY 2005_orders DESC;
查詢結果應該包含第一個資料表和第二個資料表中所有可能的列組合,也稱為笛卡兒積。不幸的是,當我們在 Navicat Premium 16 中執行査詢時,只傳回 13 列,儘管資料表中有 122 個客戶(未顯示):
為了理解哪裡出了問題,讓我們一步一步從欄和外部聯結開始重建查詢:
現在我們得到了所有的客戶。那些沒有下任何訂單的客戶的 customerNumbers 是 NULL,因為它們來自 orders 資料表。
現在,讓我們套用 WHERE 子句述詞:
突然之間,很多顧客消失了!問題是 WHERE 子句中的述詞將外部聯結變成了內部聯結。
為了糾正這個問題,我們需要將 WHERE 述詞加入聯結條件中:
我們現在可以調整原本的査詢以擷取所有客戶:
故事的重點
謹記要時刻注意篩選掉的列的位置。在上述範例中,WHERE 子句是問題所在。在一個更複雜的範例中,如果有多個聯結,錯誤的篩選可能會在後續的資料表運算子(如聯結至另一個資料表)而不是 WHERE 子句中。