SQL进阶 | CASE表达式

      本文所有案例基于《SQL进阶教程》实现。

 概述

        SQL中的CASE表达式是一种通用的条件表达式,类似于其他语言中的if/else语句。它用于在SQL语句中实现条件逻辑。CASE表达式以WHEN子句开始,后面跟着一个或多个WHEN条件,每个WHEN条件后面跟着一个THEN子句。如果任何WHEN条件为真,则返回相应的THEN子句中的表达式。如果没有任何WHEN条件为真,则可以选择性地使用ELSE子句来指定一个默认的表达式。

CASE表达式的语法如下:

-- 简单 CASE 表达式
CASE sexWHEN '1' THEN '男'WHEN '2' THEN '女'
ELSE '其他' END-- 搜索 CASE 表达式
CASE WHEN sex = '1' THEN '男'WHEN sex = '2' THEN '女'
ELSE '其他' END

        需要注意,在发现为真的 WHEN 子句时,CASE 表达式的真假值判断就会中止,而剩余的 WHEN 子句会被忽略。为了避免引起不必要的混乱,使用 WHEN 子句时要注意条件的排他性。

-- 例如,这样写的话,结果里不会出现“第二”
CASE WHEN col_1 IN ('a', 'b') THEN '第一'WHEN col_1 IN ('a') THEN '第二'
ELSE '其他' END

此外,使用 CASE 表达式的时候,还需要注意以下几点。

  • 统一各分支返回的数据类型
  • 不要忘了写 END
  • 养成写 ELSE 子句的习惯

结果转化

        例如,现在有一张按照“‘1:北海道’、‘2:青森’、……、‘47:冲绳’”
这种编号方式来统计都道府县 A 人口的表,我们需要以东北、关东、九州等地区为单位来分组,并统计人口数量。具体来说,就是统计下表 PopTbl中的内容,得出如右表“统计结果”所示的结果。

代码如下:

SELECT CASE pref_nameWHEN '德岛' THEN '四国'WHEN '香川' THEN '四国'WHEN '爱媛' THEN '四国'WHEN '高知' THEN '四国'WHEN '福冈' THEN '九州'WHEN '佐贺' THEN '九州'WHEN '长崎' THEN '九州'ELSE '其他' END AS district,SUM(population)
FROM PopTbl
-- GROUP BY 子句里引用了 SELECT 子句中定义的别名
GROUP BY district;

     

         使用case表达式能够方便的将数据库中查询到的结果转化为我们需要的结果,但是在本代码中使用到的别名进行分组,这种写法是违反标准sql的规则的。在select语句的执行流程中,group by语句会比select语句先执行,所以在group by语句中引用在select语句里定义的别称是不被允许的。

条件统计

        例如,我们需要往存储各县人口数量的表 PopTbl 里添加上“性别”列,然后求按性别、县名汇总的人数。具体来说,就是统计表 PopTbl2 中的数据,然后求出如表“统计结果”所示的结果。

代码如下:

SELECT pref_name,-- 男性人口SUM( CASE WHEN sex = '1' THEN population ELSE 0 END) AS cnt_m, -- 女性人口SUM( CASE WHEN sex = '2' THEN population ELSE 0 END) AS cnt_f FROM PopTbl2GROUP BY pref_name;

配合check约束使用

        假设某公司规定“女性员工的工资必须在 20 万日元以下”,而在这个公司的人事表中,这条无理的规定是使用 CHECK 约束来描述的,代码如下所示。

-- 代码1
CONSTRAINT check_salary CHECK( CASE WHEN sex = '2'THEN CASE WHEN salary <= 200000THEN 1 ELSE 0 ENDELSE 1 END = 1 )-- 代码2
CONSTRAINT check_salary CHECK( sex = '2' AND salary <= 200000 )

        代码1表示的含义是:限制插入“如果员工的性别为女,在此基础上判断工资是否在20万日元以下”的数据,如果员工不是女性,则不做限制。

        代码2表示的含义是:限制插入“员工必须为女性而且工资必须在20万日元以下”的数据。

        所以代码1表示的含义才是我们所需求的,这就体现出与case与check配合的独特性了。

在update语句进行条件分支

        需求:以某数值型的列的当前值为判断对象,将其更新成别的值。这里的问题是,此时UPDATE操作的条件会有多个分支。例如,我们通过下面这样一张公司人事部的员工工资信息表 Salaries 来看一下这种情况。

假设现在需要根据以下条件对该表的数据进行更新。
1. 对当前工资为 30 万日元以上的员工,降薪 10%。
2. 对当前工资为 25 万日元以上且不满 28 万日元的员工,加薪 20%。按照这些要求更新完的数据应该如下表所示。

代码如下:

-- 代码1
-- 条件 1
UPDATE SalariesSET salary = salary * 0.9WHERE salary >= 300000;
-- 条件 2
UPDATE SalariesSET salary = salary * 1.2WHERE salary >= 250000 AND salary < 280000;-- 代码2
-- 用 CASE 表达式写正确的更新操作
UPDATE SalariesSET salary = CASE WHEN salary >= 300000THEN salary * 0.9WHEN salary >= 250000 AND salary < 280000THEN salary * 1.2ELSE salary END;

         代码1使用了2条update语句,分别对这两种条件进行修改,先更新工资大于30万日元的数据,再更新25-28万日元的数据,这就会导致第一次更新之后,相田的工资已经被更新成25-28万日元之间了,第二次继续更新,影响了最终结果。所以这种更新方式不可取。

        代码2使用了case条件进行更新,这种好处是只执行1次sql,效率更高,且对数据更安全。

数据匹配

        如下所示,这里有一张资格培训学校的课程一览表和一张管理每个月所设课程的表。

我们要用这两张表来生成下面这样的交叉表,以便于一目了然地知道每个月开设的课程。

代码如下:

-- 表的匹配 :使用 IN 谓词
SELECT course_name,CASE WHEN course_id IN (SELECT course_id FROM OpenCourses WHERE month = 200706) THEN '○' ELSE '×' END AS "6 月",CASE WHEN course_id IN (SELECT course_id FROM OpenCoursesWHERE month = 200707) THEN '○' ELSE '×' END AS "7 月",CASE WHEN course_id IN (SELECT course_id FROM OpenCoursesWHERE month = 200708) THEN '○' ELSE '×' END AS "8 月"FROM CourseMaster;-- 表的匹配 :使用 EXISTS 谓词
SELECT CM.course_name,CASE WHEN EXISTS(SELECT course_id FROM OpenCourses OCWHERE month = 200706 AND OC.course_id = CM.course_id) THEN '○'ELSE '×' END AS "6 月",CASE WHEN EXISTS(SELECT course_id FROM OpenCourses OCWHERE month = 200707 AND OC.course_id = CM.course_id) THEN '○'ELSE '×' END AS "7 月",CASE WHEN EXISTS(SELECT course_id FROM OpenCourses OCWHERE month = 200708 AND OC.course_id = CM.course_id) THEN '○'ELSE '×' END AS "8 月"FROM CourseMaster CM;

         这样的查询没有进行聚合,因此也不需要排序,月份增加的时候仅修改 SELECT 子句就可以了,扩展性比较好。
        无论使用 IN 还是 EXISTS,得到的结果是一样的,但从性能方面来说,EXISTS 更好。通过 EXISTS 进行的子查询能够用到“month, course_id”这样的主键索引,因此尤其是当表 OpenCourses 里数据比较多的时候更有优势。

使用聚合函数

        假设这里有一张显示了学生及其加入的社团的一览表。如表 StudentClub 所示,这张表的主键是“学号、社团 ID”,存储了学生和社团之间多对多的关系。

        有的学生同时加入了多个社团(如学号为 100、200 的学生),有的学生只加入了某一个社团(如学号为 300、400、500 的学生)。对于加入了多个社团的学生,我们通过将其“主社团标志”列设置为 Y 或者 N 来表明哪一个社团是他的主社团;对于只加入了一个社团的学生,我们将其“主社团标志”列设置为 N。
        接下来,我们按照下面的条件查询这张表里的数据。
1. 获取只加入了一个社团的学生的社团 ID。
2. 获取加入了多个社团的学生的主社团 ID。 

SELECT std_id,CASE WHEN COUNT(*) = 1 -- 只加入了一个社团的学生THEN MAX(club_id)ELSE MAX(CASE WHEN main_club_flg = 'Y'THEN club_idELSE NULL END)END AS main_clubFROM StudentClubGROUP BY std_id;

        使用CASE 表达式表示了“只加入了一个社团还是加入了多个社团”这样的条件分支。如果只加入一个社团就获取社团id,如果加入多个社团就获取主社团id。

总结

  • 在 GROUP BY 子句里使用 CASE 表达式,可以灵活地选择作为聚合的单位的编号或等级。这一点在进行非定制化统计时能发挥巨大的威力。
  • 在聚合函数中使用 CASE 表达式,可以轻松地将行结构的数据转换成列结构的数据。
  • 相反,聚合函数也可以嵌套进 CASE 表达式里使用。
  • 相比依赖于具体数据库的函数,CASE 表达式有更强大的表达能力和更好的可移植性。
  • 正因为 CASE 表达式是一种表达式而不是语句,才有了这诸多优点。

练习题

1.用 SQL 从多行数据里选出最大值或最小值很容易——通过 GROUP BY子句对合适的列进行聚合操作,并使用 MAX 或 MIN 聚合函数就可以求出。那么,从多列数据里选出最大值该怎么做呢?

代码如下:

select gkey, case when x > y then (case when x > z then x else z end) else (case when y > z then y else z end) end as greatest
from greatests 

2.使用正文中的表 PopTbl2 作为样本数据,练习一下把行结构的数据转换为列结构的数据吧。这次请生成下面这样的表头里带有汇总和再揭的二维表。

代码如下:

select case sex when 1 then '男' else '女' end as '性别',sum(population) as '全国',sum(case when pref_name = '德岛' then population else 0 end) as '德岛',sum(case when pref_name = '香川' then population else 0 end) as '香川',sum(case when pref_name = '爱媛' then population else 0 end) as '爱媛',sum(case when pref_name = '高知' then population else 0 end) as '高知',sum(case when pref_name in ('德岛','香川','爱媛','高知') then population else 0 end) as '四国(再揭)'
from poptbl2
group by sex

3.对练习题 1 里用过的表 Greatests 正常执行 SELECT key FROM Greatests ORDER BY key;    这个查询后,结果会按照 key 这一列值的字母表顺序显示出来。
那么,请思考一个查询语句,使得结果按照 B-A-D-C 这样的指定顺序进行排列。

代码如下:

SELECT gkey 
FROM Greatests 
ORDER BY case gkey when 'B' then 1when 'A' then 2when 'D' then 3when 'C' then 4else null end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/258161.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DevEco Studio 运行项目有时会自动出现.js和.map文件

运行的时候报错了&#xff0c;发现多了.js和.map&#xff0c;而且还不是一个&#xff0c;很多个。 通过查询&#xff0c;好像是之前已知问题了&#xff0c;给的建议是手动删除(一个一个删)&#xff0c;而且有的评论还说&#xff0c;一周出现了3次&#xff0c;太可怕了。 搜的过…

IT企业使用超大数据传输最快的方式有哪些?

IT企业最核心的资产和竞争力肯定是数据&#xff0c;对于数据的安全和完整性是无比的看重的&#xff0c;那么无论是云计算、大数据分析、视频处理还是其他领域&#xff0c;IT企业都需要频繁地进行超大数据传输&#xff0c;以实现数据的共享、备份、迁移、同步等目的。那么&#…

消息队列 - RabbitMQ

消息队列 - RabbitMQ 1. 初识 MQ1.1 同步调用1.2 异步调用1.3.技术选型 2. RabbitMQ2.1 安装2.2 收发信息2.2.1 交换机(Exchange)2.2.2 队列2.2.3 绑定关系2.2.4 发送消息 2.3 数据隔离 1. 初识 MQ 微服务一旦拆分&#xff0c;必然涉及到服务之间的相互调用&#xff0c;之前讲…

FPGA入门有多难?这篇文章让你吃透零基础入门技巧!

FPGA是一个高度集成化的芯片&#xff0c;其学习过程既需要编程&#xff0c;又需要弄懂硬件电路和计算机架构。涉及到的知识和基础非常多&#xff0c;如果不合理地安排学习内容&#xff0c;学习过程会非常漫长和枯燥。这使很多想要学习FPGA小伙伴望而却步&#xff0c;那么&#…

VUE学习一、环境的安装

1.node.js安装 node.js是前端依赖的环境, 类似于java中的jdk 下载地址 node.js 下载 msi文件 下完就是一顿嘎嘎安装 , 安装后可以cmd看看node和npm的版本 1.2 yarn的安装 Yarn是Facebook最近发布的一款依赖包安装工具。Yarn是一个新的快速安全可信赖的可以替代NPM的依赖管…

OpenCL学习笔记(二)手动编译开发库(win10+vs2019)

前言 有时需求比较特别&#xff0c;可能需要重新编译opencl的sdk库。本文档简单记录下win10下&#xff0c;使用vs2019编译的过程&#xff0c;有需要的小伙伴可以参考下 一、获取源码 项目地址&#xff1a;GitHub - KhronosGroup/OpenCL-SDK: OpenCL SDK 可以直接使用git命令…

嵌入式总线技术学习(二):Modbus 总线技术详解

参考资料 工业控制网络 1. Modbus 概述 Modbus 是全球第一个真正用于工业现场的总线协议。为更好地普及和推动 Modbus 在基于以太网上的分布式应用&#xff0c;目前施耐德公司已将 Modbus 协议的所有权移交给 IDA (Interfacefor DistributedAutomation&#xff0c;分布式自动化…

EasyExcel如何实现复杂数据的导入

shigen日更文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 在我们常使用的系统中&#xff0c;难免会遇到数据导入的情况。其实导入做起来并不是很难&#xff0c;直接用到e…

注意力机制的快速学习

注意力机制的快速学习 注意力机制 将焦点聚焦在比较重要的事物上 我&#xff08;查询对象Q&#xff09;&#xff0c;这张图&#xff08;被查询对象V&#xff09; 我看一张图&#xff0c;第一眼&#xff0c;就会判断那些东西对我而言比较重要&#xff0c;那些对于我不重要&…

Stable Diffusion XL on diffusers

Stable Diffusion XL on diffusers 翻译自&#xff1a;https://huggingface.co/docs/diffusers/using-diffusers/sdxl v0.24.0 非逐字翻译 Stable Diffusion XL (SDXL) 是一个强大的图像生成模型&#xff0c;其在上一代 Stable Diffusion 的基础上主要做了如下优化&#xff1a;…

最新版IDEA专业版大学生申请免费许可证教学(无需学校教育邮箱+官方途径+非破解手段)

文章目录 前言1. 申请学籍在线验证报告2. 进入IDEA官网进行认证3. 申请 JB (IDEA) 账号4. 打开 IDEA 专业版总结 前言 当你进入本篇文章时, 你应该是已经遇到了 IDEA 社区版无法解决的问题, 或是想进一步体验 IDEA 专业版的强大. 本文是一篇学生申请IDEA免费许可证的教学, 在学…

Android之Binder原理剖析

一&#xff1a;Binder的全面介绍 binder的出现 George Hoffman当时任Be公司的工程师&#xff0c;他启动了一个名为OpenBinder 的项目&#xff0c;在Be公司被ParmSource公司收购后&#xff0c; OpenBinder 由Dinnie Hackborn继续开发&#xff0c;后来成为管理ParmOS6 Cobalt O…