CASE根据 first 条件为 true 返回一个值。- 把最具体的条件放在第一位,否则后面的条件可能永远无法达到。
- 如果没有
ELSE,则不匹配的行将返回NULL。 - 在
SUM或COUNT中,CASE 将一个查询转换为一份紧凑的数据透视式报告。
大多数报告需要一些逻辑:将订单标记为大或小,并排计算已付款订单和待处理订单,或者首先显示紧急项目。 SQL CASE WHEN 直接在查询内处理所有这些,并且它适用于所有主要数据库。
我们在 SQLite 3.50 中运行了每个查询,显示的结果来自该运行。 CASE 是标准 SQL 的一部分,因此对于这些示例,它在 PostgreSQL、MySQL、SQL Server 和 Oracle 中的行为方式相同。
什么是“案例何时”?
CASE 是一个表达式,而不是一个单独的命令。它按顺序评估条件并返回一个值,因此它可以到达允许值的任何位置:在 SELECT、WHERE、GROUP BY、ORDER BY 中或在函数内部。它总是以 END 结尾。
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
ELSE fallback
END
样本表
CREATE TABLE orders (id INT, customer TEXT, total INT, status TEXT);
INSERT INTO orders VALUES
(1,'Asha',1200,'paid'), (2,'Ben',300,'pending'),
(3,'Asha',800,'paid'), (4,'Chitra',2500,'shipped'),
(5,'Dev',150,'cancelled'), (6,'Ben',NULL,'pending');
请注意,订单 6 没有总计。该行稍后很重要。
搜索案例
这是更灵活的形式,因为每个 WHEN 都有自己的完整条件:
SELECT id, total,
CASE
WHEN total >= 1000 THEN 'large'
WHEN total >= 500 THEN 'medium'
ELSE 'small'
END AS size
FROM orders;
-- 1 | 1200 | large
-- 2 | 300 | small
-- 3 | 800 | medium
-- 4 | 2500 | large
-- 5 | 150 | small
-- 6 | NULL | small
看最后一行。订单 6 没有总数,但标记为“小”。与 NULL 的两次比较都不正确,因此落到了 ELSE。我们展示了如何在 NULL 部分修复此问题。始终使用 AS 为结果命名,否则您可能会得到一个尴尬的列名称。
简单案例
当您将一列与固定值进行比较时,较短的形式读起来更好:
SELECT id,
CASE status
WHEN 'paid' THEN 'Complete'
WHEN 'shipped' THEN 'Complete'
WHEN 'pending' THEN 'Waiting'
END AS label
FROM orders;
-- 1 | Complete
-- 2 | Waiting
-- 3 | Complete
-- 4 | Complete
-- 5 | NULL
-- 6 | Waiting
| 形式 | 比较 | 可以使用范围吗? | 最适合 |
|---|---|---|---|
| 简单 | 平等的一种表达 | 否 | 将代码映射到标签 |
| 已搜索 | 每个分支的任何条件 | 是的 | 范围、多列、NULL 检查 |
订单事宜
CASE 在第一个为真条件时停止,这是最常见的逻辑错误。交换我们之前查询中的两个条件:
SELECT id, total,
CASE
WHEN total >= 500 THEN 'medium'
WHEN total >= 1000 THEN 'large'
ELSE 'small'
END AS size
FROM orders WHERE id IN (1, 4);
-- 1 | 1200 | medium
-- 4 | 2500 | medium
现在没有什么可以是“大”的。总共 2500 也是至少 500,因此第一个分支获胜,而第二个分支永远不会达到。没有出现错误,只有错误的标签。对于范围,将最严格的条件放在前面,或者写出完整的范围,例如 total >= 500 AND total < 1000。
没有 ELSE 会发生什么?
在上面的简单 CASE 示例中,订单 5 被取消,并且没有分支处理该问题,因此结果是 NULL。有时这正是您想要的,如下一节所示。但在构建标签时,清晰的 ELSE 'other' 通常更好,因为它使意外的值可见,而不是在报告中留下空白。
提示:所有分支应返回相同的类型。在 SQLite 中混合文本和数字的工作方式比较松散,但更严格的数据库(例如 PostgreSQL)会引发错误,或者尝试以您不希望的方式转换值。
使用 SUM 的数据透视式报告
这是日常 SQL 中最有用的模式之一。将 CASE 包装在聚合中会将行转换为列,每个客户一行:
SELECT customer,
SUM(CASE WHEN status = 'paid' THEN total ELSE 0 END) AS paid,
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) AS pending_orders
FROM orders
GROUP BY customer
ORDER BY customer;
-- Asha | 2000 | 0
-- Ben | 0 | 2
-- Chitra | 0 | 0
-- Dev | 0 | 0
现在,一个查询即可提供已付费收入和挂单数量,无需单独的查询或联接。该模式几乎适用于所有数据库,与只有某些数据库提供的专用 PIVOT 语法不同。如果 GROUP BY 对您来说是新的,请阅读 SQL GROUP BY 和 HAVING。
条件计数
COUNT 会跳过 NULL,因此没有 ELSE 的 CASE 仅计算匹配的行:
SELECT COUNT(*) AS all_orders,
COUNT(CASE WHEN status = 'paid' THEN 1 END) AS paid_orders,
COUNT(CASE WHEN total >= 1000 THEN 1 END) AS large_orders
FROM orders;
-- 6 | 2 | 2
请注意:如果在 COUNT 内添加 ELSE 0,则每一行都会被计数,因为 0 不为 NULL。将 ELSE 0 与 SUM 一起使用,并与 COUNT 一起省略。 PostgreSQL 和 SQLite 也支持 COUNT(*) FILTER (WHERE ...),读起来很好,但并不是所有地方都可用。
按 CASE 分组
您可以创建自己的存储桶,然后总结它们:
SELECT CASE WHEN total >= 1000 THEN 'large' ELSE 'other' END AS bucket,
COUNT(*)
FROM orders
GROUP BY bucket
ORDER BY bucket;
-- large | 2
-- other | 4
按别名分组适用于 SQLite、PostgreSQL 和 MySQL。 SQL Server 不允许这样做,因此您必须重复整个表达式,或者首先将查询放入 CTE 或子查询中。
使用 ORDER BY 自定义排序
按字母顺序排列很少是企业想要的。 CASE 允许您定义自己的顺序:
SELECT id, status FROM orders
ORDER BY CASE status
WHEN 'pending' THEN 1
WHEN 'paid' THEN 2
ELSE 3
END, id;
-- 2 | pending
-- 6 | pending
-- 1 | paid
-- 3 | paid
-- 4 | shipped
-- 5 | cancelled
待处理订单排在第一位,这对于工作队列来说很方便。第二个排序列 id 使每组中的行保持可预测的顺序。
更新中的案例
CASE 允许一条语句将不同的更改应用于不同的行。此处,小额挂单被标记以供审核:
UPDATE orders
SET status = CASE
WHEN status = 'pending' AND total < 500 THEN 'review'
ELSE status
END;
SELECT id, status FROM orders WHERE customer = 'Ben';
-- 2 | review
-- 6 | pending
ELSE status 保持其他行不变。 忽略它是危险的,因为不匹配的行将被设置为 NULL。如果没有 WHERE 子句,此语句也会触及每一行,因此在大型表上添加一个过滤器,并考虑将更改包装在 事务 中,以便您可以在提交之前检查结果。还要注意,顺序 6 没有改变,因为它的 NULL 总数不满足 total < 500。
空陷阱
NULL 导致 CASE 出现两个不同的问题。首先,简单形式使用相等进行比较,没有任何东西等于 NULL,甚至 NULL 本身也不等于:
SELECT CASE NULL WHEN NULL THEN 'matched' ELSE 'not matched' END;
-- not matched
所以 CASE col WHEN NULL 永远不会匹配。请使用带有 IS NULL 的搜索形式。其次,正如我们在顺序 6 中看到的那样,NULL 值跳过了比较并落在 ELSE 中。修复方法是首先检查它:
SELECT id,
CASE
WHEN total IS NULL THEN 'unknown'
WHEN total >= 1000 THEN 'large'
ELSE 'other'
END AS size
FROM orders WHERE id IN (1, 2, 6);
-- 1 | large
-- 2 | other
-- 6 | unknown
Order 6 现在获得了诚实标签。我们的 SQL NULL 值指南 完整地涵盖了这种行为。
CASE 与其他选项
| 选项 | 工作于 | 当 |
|---|---|---|
案例 |
所有主要数据库 | 你想要便携式逻辑 |
合并 |
所有主要数据库 | 您只需要 NULL 的后备 |
IF() |
MySQL | 简单的双向选择 |
IIF() |
SQL Server、SQLite | 简单的双向选择 |
DECODE() |
甲骨文 | 较旧的 Oracle 代码 |
| 查找表 | 全部 | 映射很长或经常更改 |
最后一个选择值得考虑。如果 CASE 增长到 30 个分支并出现在 5 个查询中,请将映射移动到表中并 join 到它。那么更改就是单行更新,而不是五个地方的代码更改。
常见错误
- 条件顺序错误。 宽泛的条件首先隐藏了较窄的条件。
- 忘记
END。这是最常见的语法错误。 - 使用
WHEN NULL。它永远不会匹配,所以使用WHEN col IS NULL。 - 在
COUNT中添加ELSE 0。它计算所有行。 - 在更新中省略
ELSE。它可以用NULL擦除值。 WHERE中按CASE过滤。它可以阻止索引被使用。请参阅我们的 SQL 查询优化指南。
最后的话
CASE WHEN 是一个小功能,但可以完成大量工作。请记住三个规则:第一个 true 分支获胜,不匹配的行在没有 ELSE 的情况下变为 NULL,并且 NULL 需要自己的检查。为了进行练习,请获取您当前使用多个查询构建的报告,并尝试使用 SUM(CASE ...) 将其生成为一个查询。
常见问题
SQL 中 CASE WHEN 的作用是什么?
它将 if-then 逻辑添加到查询中。 SQL 按顺序检查每个条件并返回第一个为 true 的值,如果没有则返回 ELSE 值。
如果 CASE 语句中没有 ELSE 会发生什么?
不匹配任何条件的行返回 NULL。这在 COUNT 中很有用,但在标签和更新中可能会导致空白或数据丢失。
我可以在 WHERE 子句中使用 CASE 吗?
是的,因为它是一种表达。然而,简单的 AND 和 OR 条件通常更清晰,并且更有可能使用索引。
简单CASE和搜索CASE有什么区别?
简单 CASE 将一个表达式与固定值进行比较以确定是否相等。搜索的 CASE 在每个 WHEN 中都有完整的条件,因此它可以处理范围和 NULL 检查。
CASE语句可以嵌套吗?
是的,一个 CASE 可以出现在另一个 CASE 中。不过,深层嵌套很难阅读,因此请考虑使用 AND 组合条件或使用查找表。
继续学习
需要测试、WordPress 或 SEO 方面的帮助?
我们帮助团队交付更好的软件并发展他们的网站。告诉我们您正在做什么,我们将建议明确的下一步。
更多指南 测试、人工智能和现代开发
喜欢这篇文章吗?选择一个主题并直接跳至我们最新的教程。
人工智能幻觉解释:语言模型为何会产生幻觉以及如何减少它
人工智能幻觉解释:它们是什么,为什么语言模型产生它们,常见类型,如何通过基础和提示来减少它们,以及我们进行的简单检查。
阅读文章 →DevOps环境变量解释:.env 文件、秘密和配置正确完成
使用我们运行的 Python 示例解释环境变量:读取和设置它们、为什么每个值都是字符串、.env 文件、进程继承、秘密和验证。
阅读文章 →软件开发API 速率限制解释:算法、标头和处理 429 错误
API 速率限制通过我们运行的模拟进行了解释:令牌桶、固定窗口、滑动窗口、429 响应、速率限制标头、带回退的重试和测试限制。
阅读文章 →测试自动化Python 中的模拟:unittest.mock、补丁以及导致测试谎言的错误
使用unittest.mock 在Python 中进行模拟,经过测试:模拟和return_value、side_effect、修补使用名称的位置、spec 和autospec、模拟日期以及常见错误。
阅读文章 →软件测试代码覆盖率解释:行覆盖率与分支覆盖率以及 100% 的真正含义
用真实的coverage.py运行来解释代码覆盖率:行、分支和条件覆盖率,阅读报告,为什么100%覆盖率仍然会错过错误,以及合理的目标。
阅读文章 →数据库与 SQLSQL 中的 DELETE、TRUNCATE 和 DROP:差异、回滚以及何时使用它们
DELETE、TRUNCATE 和 DROP 通过经过测试的 SQL 进行了解释:各自删除什么、回滚、触发器、身份重置、速度,以及 PostgreSQL、MySQL、SQL Server 和 SQLite 有何不同。
阅读文章 →数据库与 SQLSQL 视图解释:创建、使用和避免陷阱(附示例)
SQL 视图通过我们运行的查询进行解释:创建视图、实时结果、隐藏列、性能、可更新和物化视图、破坏的依赖关系和最佳实践。
阅读文章 →软件开发TypeScript 接口与类型:真正的差异,使用编译器进行测试
TypeScript 接口与类型,使用真实编译器进行检查:两者都可以做什么,只有一个可以做什么,声明合并,扩展与交集以及要遵循的简单规则。
阅读文章 →软件开发Python *args 和 **kwargs 解释:灵活的函数和清晰的示例
Python *args 和 **kwargs 用我们运行的代码进行了解释:打包和解包、参数顺序、仅关键字和仅位置参数、装饰器和常见错误。
阅读文章 →软件开发Python Lambda 函数:语法、实际用途以及何时不使用它们
Python lambda 函数用我们运行的代码进行了解释:语法、使用键排序、映射和过滤器、闭包、后期绑定陷阱,以及何时普通 def 是更好的选择。
阅读文章 →人工智能什么是人工智能代理?它们是如何工作的,只需运行一个简单的代理循环
什么是人工智能代理?关于代理循环、工具、内存和护栏的简单指南,以及我们运行的一个小型 Python 循环、真正的风险以及何时简单的聊天机器人是更好的选择。
阅读文章 →DevOps监控与可观察性:真正的区别,包括日志、指标和跟踪
监控与可观察性解释:已知和未知的故障、日志、指标和跟踪、检查事件演练、黄金信号、OpenTelemetry 和有用的警报。
阅读文章 →DevOpsDocker Volumes 与 Bind Mounts:以正确的方式保留容器数据
Docker 卷与绑定挂载解释:为什么容器数据消失、命名卷、绑定挂载、tmpfs、Compose 示例、备份、权限以及何时使用它们。
阅读文章 →测试自动化TDD 与 BDD:主要区别、以两种方式构建的一个功能以及何时使用每种方式
TDD 与 BDD 的解释是通过一种双向构建的折扣功能:使用我们运行的代码进行红绿重构、Given-When-Then 场景、工具、权衡以及何时使用每种方案。
阅读文章 →软件测试用户验收测试 (UAT):流程、清单和真实示例
用户验收测试解释:谁运行它、它与 QA 有何不同、七步流程、UAT 测试用例模板、清单、签核标准和常见错误。
阅读文章 →数据库与 SQLSQL NULL 值解释:IS NULL、COALESCE、NULLIF 和要避免的陷阱
通过我们运行的查询解释了 SQL NULL 值:三值逻辑、IS NULL、COALESCE、NULLIF,聚合、连接和排序如何处理 NULL,以及它悄悄引起的错误。
阅读文章 →数据库与 SQLSQL CASE WHEN 语句:语法、实例和常见错误
SQL CASE WHEN 解释了我们运行的查询:简单和搜索表单、条件顺序、SUM 透视、COUNT 技巧、自定义排序、更新和要避免的 NULL 陷阱。
阅读文章 →软件开发什么是智威汤逊?通过工作示例解释 JSON Web 令牌
什么是智威汤逊? JSON Web Tokens 解释:这三个部分,在我们运行的代码中构建和篡改的令牌,JWT 与会话,存储,刷新令牌和安全错误。
阅读文章 →软件开发JavaScript 事件循环解释:调用堆栈、微任务和计时器以及示例
JavaScript 事件循环用我们运行的代码进行了解释:调用堆栈、任务和微任务队列、为什么 setTimeout 0 不是立即执行、阻塞、饥饿和面试测验。
阅读文章 →软件开发Python 生成器和 Yield 解释:通过真实示例节省内存
Python 生成器和yield 用我们运行的代码进行了解释:它们如何暂停和恢复、测量内存节省、管道、无限序列、yield from、发送和常见陷阱。
阅读文章 →人工智能什么是嵌入?向量和余弦相似度简单解释
什么是嵌入?关于向量、余弦相似度和语义搜索的简单语言指南,其中包含我们运行的一个小示例、常见用途、选择模型和已知限制。
阅读文章 →DevOps蓝绿部署与金丝雀部署:差异、权衡以及如何选择
蓝绿部署与金丝雀部署解释:每种部署的工作原理、成本、风险和回滚、我们测试的稳定用户分桶、数据库迁移、滚动更新以及如何选择。
阅读文章 →软件开发Git merge 与 rebase 的区别:示例及如何选择
用真实的 log 输出、对比表、rebase 黄金法则和安全的团队工作流,讲清 Git merge 与 rebase 的区别。
阅读文章 →软件开发Python 列表推导式:语法、示例与何时避免使用
Python 列表推导式详解:语法、过滤、if-else、嵌套循环、实测耗时,以及何时普通 for 循环更合适。
阅读文章 →数据库与 SQLSQL JOIN 详解:附示例(INNER、LEFT、RIGHT、FULL)
通过示例表和查询,一步步讲解 SQL JOIN:INNER、LEFT、RIGHT、FULL、CROSS 和 SELF,并介绍常见错误与性能技巧。
阅读文章 →