你大概早就碰過程式語言裡的條件運算式了吧:if-else、switch-case 這些東西。在 SQL 裡也有自己的條件工具:就是 CASE。它讓你可以直接在查詢裡做決策:如果條件成立,做這個,不成立就做別的。
CASE 這招特別好用,尤其是你要處理可能有 NULL 值的資料時。語法超簡單,像小抄一樣,來看看:
CASE
WHEN 條件1 THEN 結果1
WHEN 條件2 THEN 結果2
...
ELSE 預設結果
END
是不是很直覺?「如果這樣,就做這個,不然就做那個」。超好記:「WHEN 就是如果,THEN 就是要做什麼,ELSE 就是都不符合時要做什麼」。
範例:商品分類
想像一下,我們有個 products 表,裡面有個 price 欄位,我們想根據價格把商品分組。
| id | name | price |
|---|---|---|
| 1 | Magic Wand | 120 |
| 2 | Potion Set | 45 |
| 3 | Crystal Ball | 75 |
| 4 | Enchanted Map | NULL |
| 5 | Broomstick | 99 |
| 6 | Spell Book | 180 |
SELECT
name AS product_name,
price,
CASE
WHEN price IS NULL THEN '未知'
WHEN price < 50 THEN '平價'
WHEN price BETWEEN 50 AND 100 THEN '標準'
ELSE '高級'
END AS price_category
FROM products;
這裡發生了什麼?
- 如果商品的
price沒填(NULL),我們顯示分類為'未知'。 - 如果價格小於 50,這商品就算「平價」
'平價'。 - 如果價格在 50 到 100 之間,就是
'標準'。 - 其他全部都是高級貨
'高級'。
結果長這樣:
| product_name | price | price_category |
|---|---|---|
| Magic Wand | 120 | 高級 |
| Potion Set | 45 | 平價 |
| Crystal Ball | 75 | 標準 |
| Enchanted Map | NULL | 未知 |
| Broomstick | 99 | 標準 |
| Spell Book | 180 | 高級 |
SQL 就像魔法師一樣,讀每一行 products,然後幫你聰明地分類。
在 CASE WHEN 處理 NULL
資料裡常常會有漏掉的值(哈囉 NULL),這時你就要補點東西進去。比如我們有個 users 表,裡面有 email 欄位,對於沒填 email 的用戶,我們想顯示 '未填寫'。
| user_id | name | |
|---|---|---|
| 1 | Alex Lin | alex@example.com |
| 2 | Maria Chi | NULL |
| 3 | Anna Song | anna@magic.com |
| 4 | Otto Art | NULL |
| 5 | John Smith | john@wizard.org |
SELECT
user_id,
name,
CASE
WHEN email IS NULL THEN '未填寫'
ELSE email
END AS email_address
FROM users;
| user_id | name | email_address |
|---|---|---|
| 1 | Alex Lin | alex@example.com |
| 2 | Maria Chi | 未填寫 |
| 3 | Anna Song | anna@magic.com |
| 4 | Otto Art | 未填寫 |
| 5 | John Smith | john@wizard.org |
SQL 在這裡就像薩滿一樣,讓半空的欄位活過來。如果 email 沒填,就補上 '未填寫',有填就直接顯示。
數字的條件運算式
有時候你不只是要補值,還要做點新邏輯。比如我們有個 students 表,裡面有 score(分數)和 name 欄位。
| name | score |
|---|---|
| Alex Lin | 95 |
| Maria Chi | 82 |
| Anna Song | 48 |
| Otto Art | NULL |
| John Smith | 67 |
| Zoe Black | 30 |
我們想根據分數把學生分成「優秀」、「及格」和「不及格」。
SELECT
name AS student_name,
score,
CASE
WHEN score IS NULL THEN '無分數'
WHEN score >= 90 THEN '優秀'
WHEN score >= 50 THEN '及格'
ELSE '不及格'
END AS performance_category
FROM students;
| student_name | score | performance_category |
|---|---|---|
| Alex Lin | 95 | 優秀 |
| Maria Chi | 82 | 及格 |
| Anna Song | 48 | 不及格 |
| Otto Art | NULL | 無分數 |
| John Smith | 67 | 及格 |
| Zoe Black | 30 | 不及格 |
SQL 很貼心地幫學生分級,像嚴格的考官:90 分以上才「優秀」,50 分以上「及格」。分數不夠...你懂的。
分組與 NULL 處理
處理資料分組也是 CASE WHEN 超強的地方。想像我們有個 orders 表,要算每個狀態的訂單數,包括狀態是 NULL 的訂單。
| order_id | status |
|---|---|
| 1 | Completed |
| 2 | Pending |
| 3 | NULL |
| 4 | Shipped |
| 5 | Completed |
| 6 | NULL |
| 7 | Pending |
| 8 | Completed |
| 9 | Shipped |
| 10 | NULL |
SELECT
CASE
WHEN status IS NULL THEN '無狀態'
ELSE status
END AS order_status,
COUNT(*)
FROM orders
GROUP BY
CASE
WHEN status IS NULL THEN '無狀態'
ELSE status
END;
這個查詢會把 NULL 狀態補成 '無狀態',然後算每組訂單數。結果如下:
| order_status | count |
|---|---|
| Completed | 3 |
| Pending | 2 |
| Shipped | 2 |
| 無狀態 | 3 |
實戰案例:「魔術」CASE WHEN
範例 1:排序時考慮 NULL
有時候你想讓 NULL 值在排序時出現在最前或最後。像是任務清單,重要的任務要排前面,沒設定優先級(NULL)的就丟最後。
| task_id | task_name | priority |
|---|---|---|
| 1 | Fix bugs | 1 |
| 2 | Update documentation | 3 |
| 3 | Plan sprint | NULL |
| 4 | Code review | 2 |
| 5 | Organize meeting | NULL |
| 6 | Deploy release | 1 |
SELECT
task_name,
priority,
CASE
WHEN priority IS NULL THEN 1
ELSE 0
END AS priority_sort
FROM tasks
ORDER BY priority_sort ASC, priority ASC;
這裡我們加了一個「虛擬」欄位 priority_sort,讓 NULL 都排在後面,其他照順序排。
| task_name | priority | priority_sort |
|---|---|---|
| Deploy release | 1 | 0 |
| Fix bugs | 1 | 0 |
| Code review | 2 | 0 |
| Update documentation | 3 | 0 |
| Plan sprint | NULL | 1 |
| Organize meeting | NULL | 1 |
範例 2:計算時考慮 NULL
再來想像一下,我們要算訂單總金額,orders 表裡 discount(折扣)欄位有時是 NULL,代表沒折扣。
| order_id | total_price | discount |
|---|---|---|
| 101 | 100 | 10 |
| 102 | 200 | NULL |
| 103 | 150 | 15 |
| 104 | 120 | NULL |
| 105 | 80 | 5 |
我們要把 NULL 換成 0,這樣計算才不會壞掉。
SELECT
order_id,
total_price,
discount,
total_price -
CASE
WHEN discount IS NULL THEN 0
ELSE discount
END AS final_price
FROM orders;
| order_id | total_price | discount | final_price |
|---|---|---|---|
| 101 | 100 | 10 | 90 |
| 102 | 200 | NULL | 200 |
| 103 | 150 | 15 | 135 |
| 104 | 120 | NULL | 120 |
| 105 | 80 | 5 | 75 |
這個 CASE 魔法讓你不用怕 NULL 讓數學爆炸。
對於 orderid = 101: discount = 10. finalprice = 100 - 10 = 90。對於 orderid = 102: discount = NULL. CASE 回傳 0. finalprice = 200 - 0 = 200。對於 orderid = 103: discount = 15. finalprice = 150 - 15 = 135。對於 orderid = 104: discount = NULL. CASE 回傳 0. finalprice = 120 - 0 = 120。對於 orderid = 105: discount = 5. finalprice = 80 - 5 = 75。
範例 3:顯示用戶狀態
日常工作常常要顯示用戶狀態(像「啟用中」或「等待中」),或是提醒沒資料。比如 users 表有 last_login 欄位,記錄最後登入日期。
| user_id | name | last_login |
|---|---|---|
| 1 | Alex Lin | 2024-12-10 |
| 2 | Maria Chi | 2025-04-20 |
| 3 | Anna Song | NULL |
| 4 | Otto Art | 2025-05-01 |
| 5 | Liam Park | 2025-05-25 |
SELECT
user_id,
name,
CASE
WHEN last_login IS NULL THEN '從未登入'
WHEN last_login < CURRENT_DATE - INTERVAL '30 days' THEN '不活躍'
ELSE '啟用中'
END AS user_status
FROM users;
這個查詢讓管理系統活起來:沒登入過的叫「從未登入」,很久沒來的算「不活躍」,其他就是「啟用中」!
| user_id | name | user_status |
|---|---|---|
| 1 | Alex Lin | 不活躍 |
| 2 | Maria Chi | 不活躍 |
| 3 | Anna Song | 從未登入 |
| 4 | Otto Art | 不活躍 |
| 5 | Liam Park | 啟用中 |
常見錯誤與避免方法
漏掉 ELSE: 如果你沒寫 ELSE,SQL 只要沒符合條件就會回傳 NULL。這通常不是你想要的,所以最好都明確寫 ELSE,就算你覺得所有情況都包了。
CASE
WHEN condition THEN result
-- ELSE '預設值' -- 別忘了!
END
複雜條件沒加括號: 如果你有很多 AND、OR 或 NOT,一定要加括號。不然 SQL 會「想歪」喔。
CASE
WHEN (column1 IS NOT NULL AND column2 > 5) THEN '有效'
ELSE '無效'
END
處理 NULL: 記得 NULL 永遠不等於(=)任何東西。舉例:
CASE
WHEN column = NULL THEN '錯誤!' -- 錯誤寫法!
WHEN column IS NULL THEN '正確!' -- 這才對。
END
GO TO FULL VERSION