有時候資料庫裡沒有你需要的欄位,但可以從現有的欄位算出來。
比如說,老闆問你:「幫我把 20-30 歲、30-40 歲、40-50 歲、50 歲以上的會員,分別算出他們的平均消費金額。」
你心想:這不就是 GROUP BY 嗎?按照年齡區間分組就好了,小菜一碟。
但仔細一看,會員表裡面只有「年齡」欄位,沒有「年齡區間」欄位:
為什麼沒有?
根據第三正規化原則,如果一個欄位的值可以從其他欄位計算出來,就不需要額外儲存。
年齡區間可以從年齡算出來,所以資料庫裡不會有「年齡區間」這個欄位。
所以你必須自己產生「年齡區間」這個欄位。
條件判斷是什麼?
怎麼產生「年齡區間」這個欄位呢?
你需要根據「年齡」欄位的值,判斷它落在哪個區間,然後產生對應的結果:
- 如果年齡是 25,就產生 ’20-30′
- 如果年齡是 35,就產生 ’30-40′
- 如果年齡是 42,就產生 ’40-50′
- 如果年齡是 55,就產生 ’50以上’
這種「根據條件產生不同結果」的邏輯,就是條件判斷。
在其他程式語言裡,條件判斷通常用 switch 或 match 來寫。
JavaScript 的 switch:
switch (性別) {
case 'M':
return '先生';
case 'F':
return '女士';
default:
return '朋友';
}Python 的 match:
match 性別:
case 'M':
return '先生'
case 'F':
return '女士'
case _:
return '朋友'在 SQL 裡面,條件判斷用 CASE WHEN 來寫,邏輯是類似的。
CASE WHEN 的基本語法
前面提到,JavaScript 用大括號 {} 來標示程式碼區塊的範圍,Python 用縮排加上冒號來區分。
但在 SQL 裡面,很少使用括號。圓括號 () 是用來把資料放在一起,方括號和大括號幾乎不使用。
那 SQL 怎麼知道條件判斷從哪裡開始、到哪裡結束呢?
答案是:用保留字。
SQL 用 CASE 表示條件判斷開始,用 END 表示條件判斷結束:
CASE
WHEN 條件1 THEN 值1
WHEN 條件2 THEN 值2
WHEN 條件3 THEN 值3
ELSE 預設值
END中間的 WHEN 和 THEN 也是保留字:
WHEN:當什麼條件成立THEN:就返回什麼值
最後的 ELSE 是前面條件都不成立時的預設值。
整理一下:
要注意的是,當某個 WHEN 條件成立時,會直接返回值,後面的條件不會再判斷。
這就像 JavaScript 裡 return 之後就不會繼續執行一樣。
舉例來說:
CASE
WHEN 年齡 >= 20 THEN '20歲以上'
WHEN 年齡 >= 30 THEN '30歲以上'
ELSE '20歲以下'
END如果年齡是 35,第一個條件 年齡 >= 20 就成立了,會直接返回 ’20歲以上’,不會再判斷第二個條件。
所以條件的順序很重要。如果你想要正確區分年齡區間,應該把範圍小的條件放前面,或是用更精確的條件:
CASE
WHEN 年齡 >= 30 THEN '30歲以上'
WHEN 年齡 >= 20 THEN '20-30歲'
ELSE '20歲以下'
ENDCASE WHEN 和 GROUP BY 的差別
你可能會想:GROUP BY 不是也可以把資料分組嗎?
但 CASE WHEN 和 GROUP BY 做的事情不一樣:
GROUP BY:把多筆資料壓縮成一筆,原本的資料列會減少。
CASE WHEN:根據條件產生一個新的欄位,原本的資料列數量不變,每一筆資料都還在。
以這個例子來說:
用 CASE WHEN
用 CASE WHEN 產生年齡區間後:
資料還是 4 筆,只是多了一個「年齡區間」欄位。
這個欄位是暫時計算出來的,原本的「年齡」欄位還在,不會被壓縮或消失。
用 GROUP BY
如果用 GROUP BY 年齡呢?
GROUP BY 會把「年齡相同的」資料壓縮在一起。但這 4 筆資料的年齡都不同,所以還是 4 筆。
而且 GROUP BY 沒辦法幫你產生「年齡區間」這個新欄位,它只能用現有的欄位來分組。
範例:產生年齡區間欄位
老闆說:「幫我把 20-30 歲、30-40 歲、40-50 歲、50 歲以上的會員,分別算出他們的平均消費金額。」
這個問題需要分兩步。
為什麼?
因為我們要按照「年齡區間」分組,但資料庫裡沒有這個欄位。
所以要先用 CASE WHEN 產生「年齡區間」欄位。
CASE WHEN 通常會搭配 SELECT 使用,把計算出來的結果當作一個新欄位輸出。
而 SELECT 輸出的內容,可以當作一張新的表單(臨時表單),作為另一個查詢的輸入。
所以我們可以:
- 第一步:用 SELECT + CASE WHEN 產生「年齡區間」欄位,得到一張臨時表單
- 第二步:從這張臨時表單,用 GROUP BY 按照年齡區間分組,搭配 AVG 計算平均消費金額
第一步:產生年齡區間欄位
首先,寫出 CASE WHEN 條件判斷:
CASE
WHEN 年齡 >= 20 AND 年齡 < 30 THEN '20-30'
WHEN 年齡 >= 30 AND 年齡 < 40 THEN '30-40'
WHEN 年齡 >= 40 AND 年齡 < 50 THEN '40-50'
WHEN 年齡 >= 50 THEN '50以上'
ELSE '20以下'
END AS 年齡區間這段條件判斷的邏輯是:
- 如果年齡在 20-30 之間,返回 ’20-30′
- 如果年齡在 30-40 之間,返回 ’30-40′
- 如果年齡在 40-50 之間,返回 ’40-50′
- 如果年齡 50 以上,返回 ’50以上’
- 其他情況(20 歲以下),返回 ’20以下’
最後用 AS 年齡區間 給這個新欄位取名字。
接著,把這段 CASE WHEN 放進 SELECT 裡面。
因為我們要把計算出來的「年齡區間」輸出成一個新欄位,這樣才能產生一張新的表單,讓第二步可以繼續使用。
SELECT
會員,
金額,
【CASE WHEN 條件判斷】
FROM 會員訂單表組合起來:
SELECT
會員,
金額,
CASE
WHEN 年齡 >= 20 AND 年齡 < 30 THEN '20-30'
WHEN 年齡 >= 30 AND 年齡 < 40 THEN '30-40'
WHEN 年齡 >= 40 AND 年齡 < 50 THEN '40-50'
WHEN 年齡 >= 50 THEN '50以上'
ELSE '20以下'
END AS 年齡區間
FROM 會員訂單表執行結果:
原本的表單:
執行後會得到:
這裡我們只 SELECT 需要的欄位(會員、金額、年齡區間),所以輸出不會有年齡欄位。
這樣就產生了一個原本不存在的「年齡區間」欄位。
第二步:按照年齡區間分組
第一步的結果是一張臨時表單,包含「會員」、「金額」、「年齡區間」三個欄位。
我們要從這張臨時表單,按照年齡區間分組,計算平均消費金額。
首先,把第一步的查詢當作資料來源,放在 FROM 後面:
SELECT ...
FROM (第一步的查詢) AS 會員年齡區間表這裡要用 AS 會員年齡區間表 給這張臨時表單取個名字。
接著,加上 GROUP BY 和 AVG:
SELECT 年齡區間, AVG(金額) AS 平均消費金額
FROM (第一步的查詢) AS 會員年齡區間表
GROUP BY 年齡區間組合起來:
SELECT 年齡區間, AVG(金額) AS 平均消費金額
FROM (
SELECT
會員,
金額,
CASE
WHEN 年齡 >= 20 AND 年齡 < 30 THEN '20-30'
WHEN 年齡 >= 30 AND 年齡 < 40 THEN '30-40'
WHEN 年齡 >= 40 AND 年齡 < 50 THEN '40-50'
WHEN 年齡 >= 50 THEN '50以上'
ELSE '20以下'
END AS 年齡區間
FROM 會員訂單表
) AS 會員年齡區間表
GROUP BY 年齡區間執行結果:
第一步的結果(作為輸入):
執行後會得到:
這樣就完成了老闆的需求。
CASE WHEN 的簡化寫法
如果條件是判斷某個欄位「等於」某個值,可以用簡化寫法:
CASE 欄位
WHEN 值1 THEN 結果1
WHEN 值2 THEN 結果2
ELSE 預設值
END這個寫法更像其他程式語言的 switch-case。
範例:根據性別產生稱呼
假設要寫信給會員,需要根據性別來決定稱呼。
SELECT
會員,
CASE 性別
WHEN 'M' THEN '先生'
WHEN 'F' THEN '女士'
ELSE '朋友'
END AS 稱呼
FROM 會員表這段 SQL 的邏輯是:
- 如果性別是 ‘M’(男),稱呼是「先生」
- 如果性別是 ‘F’(女),稱呼是「女士」
- 其他情況(性別是 NULL 或其他值),稱呼是「朋友」
執行後會得到:
CASE WHEN 可以放在哪裡?
CASE WHEN 最常放在 SELECT 裡面,用來產生新的欄位。
但它也可以放在其他地方,例如:
放在 WHERE 裡面:
SELECT *
FROM 訂單
WHERE CASE
WHEN 會員等級 = 'VIP' THEN 金額 > 10000
ELSE 金額 > 50000
END放在 ORDER BY 裡面:
SELECT *
FROM 會員
ORDER BY CASE 性別
WHEN 'F' THEN 1
WHEN 'M' THEN 2
ELSE 3
END不過最常見的用法還是放在 SELECT 裡面產生新欄位。
小結
這篇文章介紹了 CASE WHEN 條件判斷:
- 為什麼需要條件判斷:有時候資料庫裡沒有你需要的欄位,需要透過條件判斷來產生
- 基本語法:CASE WHEN 條件 THEN 值 ELSE 預設值 END
- 簡化寫法:當條件是判斷欄位等於某個值時,可以用 CASE 欄位 WHEN 值 THEN 結果 的寫法
- 搭配 GROUP BY:可以先用 CASE WHEN 產生新欄位,再按照這個欄位分組