SQL CASE WHEN:查询中的条件逻辑
第 3.5 课 · 阅读时间:约 7 分钟 · 练习题 ↓
SQL CASE 表达式为查询添加“如果-那么-否则”逻辑:它按顺序检查条件,并返回第一个为真的条件所对应的值。在本课中,你将学习 CASE 的两种形式、如何在 SELECT 中创建分类和标签、如何按自定义优先级排序,以及如何在 WHERE 中使用 CASE。
前面课程中的函数对每一行都做同样的转换。但规则常常取决于数据本身:低于 2 美元的付款算“小额”,高于 6 美元算“大额”;没有归还日期的租赁算“未归还”。CASE 让你可以直接在 SQL 中写出这些规则,而不必放到应用程序代码里处理。
CASE 语法
CASE 有两种形式。
搜索 CASE
每个 WHEN 都包含自己的条件。这是更灵活、也更常用的形式:
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
ELSE default_result
END
简单 CASE
将一个表达式与一组值进行比较:
CASE expression
WHEN value1 THEN result1
WHEN value2 THEN result2
ELSE default_result
END
CASE 的求值方式
- 条件从上到下依次检查。
- 返回第一个为真的条件的结果;其余条件不再检查。
- 如果没有条件为真,则返回
ELSE的值。 - 如果没有
ELSE,结果为NULL。
CASE 是一个表达式,而不是独立的语句:它返回一个值,可以用在任何允许使用值的地方,例如 SELECT、WHERE 或 ORDER BY。
SELECT 中的 CASE:分类和标签
付款级别
SELECT payment_id,
amount,
CASE
WHEN amount < 2 THEN 'Low'
WHEN amount < 6 THEN 'Medium'
ELSE 'High'
END AS payment_level
FROM payment
LIMIT 10;
结果:十笔付款,每笔都有一个级别:低于 2 美元为“Low”,2 美元到 6 美元以下为“Medium”,6 美元及以上为“High”。
条件的顺序很重要。1 美元的付款也满足 amount < 6,但它得到的是“Low”,因为第一个匹配的分支胜出。正因如此,第二个条件不需要写下限。
可读的状态
SELECT rental_id,
rental_date,
CASE
WHEN return_date IS NULL THEN 'Not returned'
ELSE 'Returned'
END AS rental_status
FROM rental
ORDER BY rental_date DESC
LIMIT 10;
结果:最近的租赁记录,用文本状态代替空的或已填写的归还日期。
用简单 CASE 处理代码
当列中存放的是代码时,简单形式很方便:
SELECT title,
rating,
CASE rating
WHEN 'G' THEN 'General audiences'
WHEN 'PG' THEN 'Parental guidance'
WHEN 'PG-13' THEN 'Parents strongly cautioned'
WHEN 'R' THEN 'Restricted'
ELSE 'Adults only'
END AS rating_description
FROM film
LIMIT 10;
结果:电影及其分级的可读说明。
ORDER BY 中的 CASE:自定义排序
分级代码的字母顺序(G、NC-17、PG、PG-13、R)与它们的含义并不一致。CASE 可以把每个代码转换为一个优先级数字:
SELECT title, rating
FROM film
ORDER BY CASE rating
WHEN 'G' THEN 1
WHEN 'PG' THEN 2
WHEN 'PG-13' THEN 3
WHEN 'R' THEN 4
ELSE 5
END,
title;
结果:电影从最宽松的分级排到最严格的分级;同一分级内按片名排序。
同样的技巧可以把重要的行放到最前面:
SELECT rental_id, rental_date, return_date
FROM rental
ORDER BY CASE WHEN return_date IS NULL THEN 0 ELSE 1 END,
rental_date DESC
LIMIT 20;
结果:先显示未归还的租赁,然后是其余租赁;每组内最新的排在前面。
WHERE 中的 CASE
CASE 可以为条件返回一个值,例如取决于另一列的阈值:
SELECT payment_id, staff_id, amount
FROM payment
WHERE amount >= CASE
WHEN staff_id = 1 THEN 5
ELSE 3
END
LIMIT 10;
结果:员工 1 经手的金额不低于 5 美元的付款,以及其他员工经手的金额不低于 3 美元的付款。
大多数这类过滤条件也可以用 AND 和 OR 来写:WHERE (staff_id = 1 AND amount >= 5) OR (staff_id <> 1 AND amount >= 3)。选择更易读的写法:两个分支时通常用 OR,分支很多时用 CASE。
实用建议
- 始终写上
ELSE。没有它,意料之外的值会变成NULL,这样的行很容易被忽略。 - 返回同一种数据类型。在不同分支中混用文本和数字会导致隐式转换和意外结果。
- 按从具体到一般的顺序排列条件。第一个为真的分支胜出。
- 在第 4.4 课中,你将在聚合函数内部使用
CASE,用一个查询按类别统计行数。
要点:
CASE返回第一个为真的WHEN条件的结果;如果都不为真,则返回ELSE。- 搜索
CASE可以检查任意条件;简单CASE把一个表达式与若干值比较。 - 没有
ELSE时,结果为NULL。 - 在
SELECT中,CASE用于创建分类和可读的标签。 - 在
ORDER BY中,它可以设置自定义排序;在WHERE中,它可以让过滤条件依赖于其他列。
常见问题
简单 CASE 和搜索 CASE 有什么区别?
简单 CASE 把一个表达式与一组值比较:CASE rating WHEN 'G' THEN ...。搜索 CASE 在每个分支中检查独立的条件:CASE WHEN amount < 2 THEN ...。搜索 CASE 能做简单形式能做的一切,还能处理范围、NULL 检查以及涉及多列的条件。
如果没有条件为真且没有 ELSE,CASE 会返回什么?
NULL。为避免意外,请始终添加一个带有明确默认值的 ELSE 分支。
SQL 中有 IF 函数吗?
标准做法是使用 CASE。MySQL 和 MariaDB 还有 IF(condition, value_if_true, value_if_false),SQL Server 有 IIF(),但它们不可移植。