上一篇我們學了「先計算」的做法,把子查詢放在 WHERE 裡面。
這篇要來學「先合併」的做法,把兩張表合併成一張大表,再做計算。
我們會從一個很笨、很暴力的方式開始,讓你理解合併的底層原理。
FROM 後面放兩張表會發生什麼事?
之前我們學的查詢,FROM 後面都只放一張表:
SELECT * FROM 會員但其實 FROM 後面可以放很多張表,用逗號隔開:
SELECT * FROM 會員, 訂單這樣寫的意思是:「我要從會員表和訂單表撈資料」。
聽起來很直覺對吧?
但這樣寫會發生一件事:排列組合。
當你寫 FROM 會員, 訂單 的時候,資料庫會把兩張表的資料做完整的排列組合。
假設會員表有 3 筆資料(John、Mary、Tom),訂單表也有 3 筆資料。
資料庫會把每一個會員配上每一筆訂單:
結果是 3 × 3 = 9 筆資料。
這在數學上叫做「笛卡爾積」(Cartesian Product),就是把兩邊所有的元素做完整的排列組合。
為什麼會這樣?
因為你沒有告訴資料庫「合併條件」。
資料庫不知道「John 的訂單是哪幾筆」,所以它只好把所有可能的組合都列出來。
加上合併條件:用 WHERE 篩選
現在我們有了一張 9 筆資料的大表,但裡面有很多「配錯」的組合。
例如「John 配上 Mary 的訂單」,這是不對的。
怎麼辦?用 WHERE 篩選出正確的組合:
SELECT *
FROM 會員, 訂單
WHERE 會員.會員ID = 訂單.會員ID這個條件會篩選出「會員表的會員 ID 等於訂單表的會員 ID」的資料列。
我們來看看 9 筆資料中,哪些符合這個條件:
只有「會員ID = 訂單.會員ID」的那 3 筆打勾。
篩選後的結果:
這就是我們想要的合併結果!
用合併後的表計算平均客單價
現在我們會合併表了,接下來把任務做完。
老闆說:「幫我找出平均客單價 5 萬以上的會員,然後給我他們的信箱。」
第一步:合併兩張表,並計算平均客單價
SELECT *,
AVG(金額) OVER (PARTITION BY 會員.會員ID) AS 客單價
FROM 會員, 訂單
WHERE 會員.會員ID = 訂單.會員ID這裡用了窗函數 OVER (PARTITION BY ...) 來計算每個會員的平均客單價。
執行後會得到一張包含「客單價」欄位的大表:
你會發現 John 的兩筆訂單,客單價都是 40,000(因為是平均值)。
第二步:篩選出平均客單價超過 5 萬的會員
現在我們有了一張包含客單價的表,接下來要篩選出客單價超過 5 萬的會員。
第一步的查詢產生了一張暫時性表格,我們可以把它放進 FROM 後面:
SELECT 姓名, 信箱
FROM (第一步的查詢) AS 會員_訂單
WHERE 客單價 > 50000這裡的 AS 會員_訂單 是幫這張暫時性表格取一個名字。
SQL 規定子查詢放在 FROM 後面時,一定要取別名,不然會報錯。
完整的查詢
把第一步的查詢帶入:
SELECT 姓名, 信箱
FROM (
SELECT *,
AVG(金額) OVER (PARTITION BY 會員.會員ID) AS 客單價
FROM 會員, 訂單
WHERE 會員.會員ID = 訂單.會員ID
) AS 會員_訂單
WHERE 客單價 > 50000執行後會得到:
任務完成!
如果老闆加條件呢?
老闆突然說:「等一下,單筆金額 2 萬 5 以下的訂單不要算進去,我只想看大單的平均客單價。」
意思是:計算平均客單價的時候,要先排除掉金額太小的訂單。
第一步:合併兩張表,並排除小單
以我們的資料為例,哪些訂單符合條件?
現在 WHERE 要同時滿足兩個條件:滿足合併條件 AND 金額 >= 25000。
只有第 1 筆和第 6 筆兩個條件都打勾。
John 的第二筆訂單(20,000)雖然符合合併條件,但金額太小被排除了。
篩選後的結果:
接下來計算平均客單價。
因為 John 只剩一筆訂單,平均客單價就是 60,000(不是之前的 40,000)。
Mary 也是一筆,平均客單價是 55,000。
計算後的結果:
第二步:篩選出平均客單價超過 5 萬的會員
現在兩個人的客單價都超過 5 萬了!
所以最後的結果會有兩個人:John 和 Mary。
完整的查詢:
SELECT 姓名, 信箱
FROM (
SELECT *,
AVG(金額) OVER (PARTITION BY 會員.會員ID) AS 客單價
FROM 會員, 訂單
WHERE 會員.會員ID = 訂單.會員ID
AND 訂單.金額 >= 25000
) AS 會員_訂單
WHERE 客單價 > 50000執行後會得到:
結果是對的,但你有沒有發現一個問題?
這個寫法有點醜
看一下 WHERE 的部分:
WHERE 會員.會員ID = 訂單.會員ID
AND 訂單.金額 >= 25000這裡混在一起的有兩種條件:
- 合併條件:
會員.會員ID = 訂單.會員ID— 決定兩張表怎麼接在一起 - 篩選條件:
訂單.金額 >= 25000— 決定要保留哪些資料
這兩種條件的「目的」完全不同,但現在都塞在同一個 WHERE 裡面。
當條件越來越多的時候,問題就來了。
想像一下,如果老闆又加了更多條件:
WHERE 會員.會員ID = 訂單.會員ID
AND 訂單.金額 >= 25000
AND 會員.註冊日期 >= '2024-01-01'
AND 訂單.訂單日期 >= '2024-06-01'
AND 會員.等級 = 'VIP'你要從這一堆 AND 裡面分辨:
- 哪個是合併條件?
- 哪個是篩選條件?
未來要修改或維護的時候,想到就覺得很痛苦。
我們需要一個專門的合併指令
如果能把「合併條件」和「篩選條件」分開寫,程式碼會更清楚:
- 合併的歸合併
- 篩選的歸篩選
這就是為什麼我們需要學 JOIN 指令。
小結
這篇學到的重點:
FROM 表A, 表B會產生排列組合(笛卡爾積)- 用
WHERE加上合併條件,可以篩選出正確的組合 - 這個方法可行,但合併條件和篩選條件混在一起,不好維護
- 下一篇會學 JOIN 指令,讓合併條件和篩選條件分開
記住一件重要的事:JOIN 指令底層做的事情,跟這篇的笨方法一模一樣。
都是先排列組合,再用條件篩選。
只是 JOIN 讓你寫起來更清楚、更好維護而已。
下一篇,我們來學真正的 JOIN 語法!