SQL进阶 | CASE表达式

news2024/11/18 19:39:13

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

 概述

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

CASE表达式的语法如下:

-- 简单 CASE 表达式
CASE sex
 WHEN '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_name
 WHEN '德岛' 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 PopTbl2
 GROUP BY pref_name;

配合check约束使用

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

-- 代码1
CONSTRAINT check_salary CHECK
 ( CASE WHEN sex = '2'
 THEN CASE WHEN salary <= 200000
 THEN 1 ELSE 0 END
 ELSE 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 Salaries
 SET salary = salary * 0.9
 WHERE salary >= 300000;
-- 条件 2
UPDATE Salaries
 SET salary = salary * 1.2
 WHERE salary >= 250000 AND salary < 280000;

-- 代码2
-- 用 CASE 表达式写正确的更新操作
UPDATE Salaries
 SET salary = CASE WHEN salary >= 300000
 THEN salary * 0.9
 WHEN salary >= 250000 AND salary < 280000
 THEN salary * 1.2
 ELSE 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 OpenCourses
 WHERE month = 200707) THEN '○' ELSE '×' END AS "7 月",
 CASE WHEN course_id IN 
 (SELECT course_id FROM OpenCourses
 WHERE month = 200708) THEN '○' ELSE '×' END AS "8 月"
 FROM CourseMaster;


-- 表的匹配 :使用 EXISTS 谓词
SELECT CM.course_name,
 CASE WHEN EXISTS
 (SELECT course_id FROM OpenCourses OC
 WHERE month = 200706 AND OC.course_id = CM.course_id) THEN '○'
 ELSE '×' END AS "6 月",
 CASE WHEN EXISTS
 (SELECT course_id FROM OpenCourses OC
 WHERE month = 200707 AND OC.course_id = CM.course_id) THEN '○'
 ELSE '×' END AS "7 月",
 CASE WHEN EXISTS
 (SELECT course_id FROM OpenCourses OC
 WHERE 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_id
 ELSE NULL END)
 END AS main_club
 FROM StudentClub
 GROUP 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 1
					when 'A' then 2
					when 'D' then 3
					when 'C' then 4
					else null end

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

实战:Docker Compose 下 Nginx、Java、Mysql 和 Redis 服务协同部署(包含解决浏览器访问Linux部署服务器本地资源问题)

1. 背景 在该实战中&#xff0c;我们将探讨如何使用Docker Compose协同部署Nginx、Java、Mysql和Redis服务&#xff0c;实现一个视频上传与展示的应用。具体需求如下&#xff1a; Java应用负责上传视频和图片资源到Nginx目录下&#xff0c;作为资源服务器。Nginx服务作为静态…

C++相关闲碎记录(5)

1、容器提供的类型 2、Array Array大小固定&#xff0c;只允许替换元素的值&#xff0c;不能增加或者移除元素改变大小。Array是一种有序集合&#xff0c;支持随机访问。 std::array<int, 4> x; //elements of x have undefined value std::array<int, 5> x {…

cpu 300% 爆满 内存占用不高 排查

top查询 cpu最高的PID ps -ef | grep PID 查看具体哪一个jar服务 jstack -l PID > ./jstack.log 下载/打印进程的线程栈信息 可以加信息简单分析 或进一步 查看堆内存使用情况 jmap -heap Java进程id jstack.log 信息示例 Full thread dump Java HotSpot(TM) 64-Bit Se…

陪诊软件开发|北京陪诊系统功能详解

在这个快节奏的生活中&#xff0c;寻求医疗服务往往让人感到繁琐和时间浪费。然而&#xff0c;现如今&#xff0c;随着科技的不断进步&#xff0c;一项创新的上门服务系统正在改变传统的医疗体验&#xff0c;带来了前所未有的便利和舒适。 陪诊系统功能&#xff1a; 1、诊前约…

海上液化天然气 LNG 终端 | 图扑数字孪生

关于 LNG 液化天然气 (Liquefied Natural Gas&#xff0c;简称 LNG) 在能源转型过程中被广泛认可为相对较清洁的能源选择。 相对于传统的煤炭和石油燃料&#xff0c;LNG 的燃烧过程产生的二氧化碳 (CO2) 排放较低。LNG 的燃烧释放的二氧化碳排放较少&#xff0c;因此对应对气…

Theamleaf导出pdf模版编写(原始th/td编写表格)

需求&#xff1a;简单的theamleaf编写表格就是简单的th/td&#xff0c;新需求是导出的模版是学员table表&#xff0c;每个项目的学员数量是不定的&#xff0c;所以用到 <tr th:each"item,start:${studentList}"> 所有代码&#xff1a; <!DOCTYPE html>…

12月7日作业

使用QT模仿一个登陆界面&#xff08;模仿育碧Ubisoft登录界面&#xff09; #include "myqq.h"MyQQ::MyQQ(QWidget *parent): QMainWindow(parent) {this->resize(880,550); //设置窗口大小this->setFixedSize(880,550); //固定窗口大小this->setStyleShee…

【Docker二】docker网络模式、网络通信、数据管理、资源控制

目录 一、docker网络模式&#xff1a; 1、概述 2、docker网络实现原理&#xff1a; 3、docker的网络模式&#xff1a; 3.1、bridge模式&#xff1a; 3.2、host模式&#xff1a; 3.3、container模式&#xff1a; 3.4、none模式&#xff1a; 3.5、自定义网络模式&#xf…

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

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

docker:部署java Springboot项目

文章目录 1、打 jar 包1、创建Dockerfile3、创建镜像4、启动容器其他注意事项docker中jdk的版本命名举例&#xff1a;openjdk:11-ea-17-jre-slim举例&#xff1a;8u312-jre-nanoserver-1809 通过find找文件 1、打 jar 包 将项目打一个 jar 包&#xff0c;可以使用 IDEA 1、…

智能优化算法应用:基于跳蛛算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于跳蛛算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于跳蛛算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.跳蛛算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…

前端依赖下载速度过慢解决方法,nrm 镜像管理工具

npm 默认镜像 &#xff1a;https://registry.npmjs.org/ 问题 使用 npm install 安装依赖的时候&#xff0c;受网络的限制&#xff0c;速度会很慢。 解决 使用国内镜像代理。 nrm nrm 是镜像源管理工具&#xff1b; 1. 安装 nrm npm install nrm --global# 查看镜像源列…

Java线程安全问题及其三大线程同步“锁”方案

文章目录 一、 线程安全问题概述二、线程安全问题的demo演示三、线程同步方案四、线程同步代码块五、同步方法六、Lock锁七、附录—多线程常用方法 在实际开发过程中&#xff0c;使用线程时最重要的一个问题非线程安全问题莫属。这篇博客会带你由浅入深的初步了解该问题&#x…

FolkMQ 内存型消息中间件,v1.0.18 发布

简介 采用 “内存运行” “快照持久化” “Broker 集群模式”&#xff08;可选&#xff09;基于 Socket.D 网络应用协议 开发&#xff0c;使用“多路复用”技术。全新设计&#xff0c;自主架构&#xff01; 角色功能生产端发布消息&#xff08;Qos0、Qos1&#xff09;、发布…

SOLIDWORKS参数化工具如何设置部分提取

编制参数表是参数化设置必不可少的一环&#xff0c;提取零部件参数又是生成参数表所必须的步骤&#xff0c;然而很多时候&#xff0c;模型的量级很大&#xff0c;需要变化的零部件只有三分之一&#xff0c;那如果全部提取出来&#xff0c;将耗费大量的时间&#xff0c;因此部分…

Swagger页面报错Resolver error at definitions

问题描述 打开swagger页面报错Resolver error at definitions 原因分析&#xff1a; 从错误提示可以看出&#xff0c;是由map引起的原因&#xff0c;具体是因为swagger配置没有默认添加map的复杂结构引起的&#xff0c;需要手动添加。 解决方案&#xff1a; 找到swagger配置类…

黄金代理商怎么做才会受客户青睐?

黄金代理商是市场中推广现货黄金投资的角色&#xff0c;黄金代理商做的越好&#xff0c;推广的效果越明显&#xff0c;投资者就越多&#xff0c;这样他们就可以获得多一点黄金代理平台的佣金。所以&#xff0c;黄金代理商们会使劲浑身解数&#xff0c;让自己获得更多客户的青睐…

【ARM Trace32(劳特巴赫) 使用介绍 13 -- Trace32 Var 变量篇】

文章目录 Trace32 查看变量值Var.view 查看变量值Var.view 查看数据类型的大小Var.view 根据变量地址查看变量值 地址类型判断 Trace32 查看变量值 步骤1 步骤2 步骤3&#xff1a; 步骤4&#xff1a; 查看结构体变量 str_t32 的值 struct t32_str {uint32_t t32_val…

【JavaEE进阶】 Spring使用注解存储对象

文章目录 &#x1f334;序言&#x1f340;前置⼯作&#xff1a;配置扫描路径&#x1f384;添加注解存储 Bean 对象&#x1f333;类注解&#x1f6a9;为什么要这么多类注解&#x1f6a9;注解之间的联系 &#x1f38b;⽅法注解 Bean&#x1f6a9;⽅法注解需要配合类注解使⽤ ⭕总…

合合信息旗下启信宝与鹏城实验室达成数据托管合作,“AI靶场”让数据管理更精准

数字经济时代&#xff0c;数据已成为新型生产要素。通过“数据托管”等形式对数据进行集中管理&#xff0c;有助于保护数据主体权益&#xff0c;促进数据共享和运用效率&#xff0c;对数字经济的发展具有重要意义。近期&#xff0c;在深圳数据交易所&#xff08;简称“深数所”…