CodeGym /课程 /SQL SELF /条件表达式:CASE WHEN ... THEN ... ELSE ... END

条件表达式:CASE WHEN ... THEN ... ELSE ... END

SQL SELF
第 10 级 , 课程 0
可用

你大概率已经在编程语言里见过条件表达式了,比如 if-elseswitch-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;

这里发生了啥?

  1. 如果商品的 price 没填(NULL),我们显示分类为 '未知'
  2. 如果价格小于 50,这个商品算低价 '低价'
  3. 如果价格在 50 到 100 之间,就是 '标准'
  4. 剩下的都是高端商品 '高端'

结果如下:

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

数据里经常会有缺失值(hello,NULL),得用别的东西替换。比如我们有个 users 表,有 email 列,想让没邮箱的用户显示 '未填写'

user_id name email
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,没折扣时就是 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

复杂条件不加括号: 如果你有多个 ANDORNOT,一定要用括号。不然 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
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION