SQL进阶 | CASE表达式

      本文所有案例基于《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.mfbz.cn/a/236794.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【S32DS RTD实战】-1.3-S32K3工程生成S19,BIN,Hex文件,以及Post-build steps的妙用

目录 1 方法一&#xff1a;逐个生成Motorola S-record&#xff08;s19&#xff0c;srec…&#xff09;&#xff0c;Intel HEX&#xff0c;Bin文件 1.1 生成Motorola S-record&#xff08;s19&#xff0c;srec…&#xff09;文件 1.2 生成Intel HEX文件 1.3 生成Bin文件 2 …

关于 SAP S/4HANA 中的控制您应该了解什么-Part1

原文地址&#xff1a;What you should know about controlling in SAP S/4HANA. (Part 1) | SAP Blogs &#xff08;自 SAP S/4HANA 版本 1909 起更新&#xff09; 作为一名CO顾问&#xff0c;我对 SAP ERP 中央组件 (ECC) 向 SAP S/4HANA 的演变感到非常兴奋。 自从第一个版…

深入理解JVM虚拟机第二十八篇:详解JVM当中方法重写的本质和虚方法表

&#x1f609;&#x1f609; 欢迎加入我们的学习交流群呀&#xff1a; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 &#x1f96d;&#x1f96d;3&#xff1a;QQ群&a…

Unity中结构体定义的成员如何显示在窗口中

在Unity中&#xff0c;有时候我们在处理数据的时候会用到结构体定义一些Unity组件相关的数据成员&#xff0c;并且需要在编辑器中拉取对象赋值。比如&#xff1a; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;publ…

正则表达式(9):扩展正则表达式

正则表达式&#xff08;9&#xff09;&#xff1a;扩展正则表达式 小结 本博文转载自 前文中一直在说&#xff0c;在Linux中&#xff0c;正则表达式可以分为”基本正则表达式”和”扩展正则表达式”。 我们已经认识了”基本正则表达式”&#xff0c;现在&#xff0c;我们来认…

笔记本电脑安装了Ubuntu系统设置关盖/合盖不挂起/不睡眠

文章目录 简介通过gnome-tweaks设置通过更改登录配置文件logind.conf设置参考资料 简介 学习工作中需要用到笔记本安装Ubuntu Linux系统&#xff0c;并且需要关盖电脑不关机、不挂起且不睡眠。为此&#xff0c;本篇博客整理了两种常规操作方式&#xff0c;并给了详细的步骤&am…

windows10安装MongoDB的入门简易教程【学习自用】

一、下载安装包 下载地址 https://www.mongodb.com/try/download/community 因为我是在windows上安装&#xff0c;所以下载windows版本的压缩包格式 二、下载后解压到要安装的目录里去 我这里在D盘装&#xff0c;所以就解压到D盘中我自己建的的mongoDB文件夹中。 三、在解…

网络安全攻击预警/态势预测算法汇总

总结&#xff1a; 网络安全攻击预警/态势预测算法众多&#xff0c;主要包括&#xff1a; 基于统计学的算法&#xff1a;协方差矩阵、马尔可夫模型等&#xff1b; 基于机器学习的算法&#xff1a;贝叶斯网络、聚类算法、支持向量机SVM、遗传算法、层次分析法AHP、决策树等&am…

LeetCode 每日一题 Day 10 || 并查集/二分搜索

1631. 最小体力消耗路径 你准备参加一场远足活动。给你一个二维 rows x columns 的地图 heights &#xff0c;其中 heights[row][col] 表示格子 (row, col) 的高度。一开始你在最左上角的格子 (0, 0) &#xff0c;且你希望去最右下角的格子 (rows-1, columns-1) &#xff08;注…

案例056:基于微信小程序的购物系统

文末获取源码 开发语言&#xff1a;PHP 框架&#xff1a;Think PHP 数据库&#xff1a;mysql 5.7 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序运行软件&#xff1a;微信开发者工具 目录 前言 系统展示 后台模块的实现 用户信息管理 商品信…

USB Type-C一拖二线缆制作方法

1 实现方法 Figure 1-1 Type-C Socket(母口) Figure 1-2 Type-C Plug(公头) Table 1-1 Type-C Socket Pin连接描述 Type-C Plug连接&#xff0c; 需要做一个一拖二的线&#xff0c;一根的一端是USB&#xff0c; 另外一根的一端是USB转UART&#xff0c; 参考Table 1-2。 Table 1…

windows MYSQL解决中文乱码问题

1.首先确保你已经把mysql配置了环境变量 2.打开window终端 3.输入mysql -u root -p 4.输入密码&#xff0c;就是安装的时候设置的root超级管理员权限密码 5.输入&#xff1a; SHOW VARIABLES LIKE ‘character%’; 出现上图&#xff0c;说明就会出现中文乱码问题。 6.该怎么办…

31、卷积 - 参数 dilation 以及空洞卷积

在卷积算法中,还有一个不常见的参数叫做dilation(中文:膨胀)。 很多同学可能没听说过这个参数,下面看看这个参数有什么作用,用来控制什么的。 我们还是放这个经典的卷积运算图,图中是看不出 dilation 这个参数的存在的。 如果再换一张图呢,发现两图的区别了吗? 没错…

JavaDay17

创建不可变集合 import java.util.Iterator; import java.util.List;public class Test {public static void main(String[] args) {/*创建不可变的List集合* "张三" "李四" "王五" "赵六*///一旦创建之后 是无法进行修改的 在下面的代码…

[Linux] nginx防盗链与优化

一、Nginx的页面优化 1.1 Nginx的网页压缩 在Nginx的ngx_http_gzip_module压缩模块提供对文件内容压缩的功能。进行相关的配置修改&#xff0c;就能实现Nginx页面的压缩&#xff0c;达到节约带宽&#xff0c;提升用户访问速度 vim /usr/local/nginx/conf/nginx.conf http { .…

Shell三剑客:正则表达式简介

前言 一、名称解释 正则表达式&#xff08;regular expression&#xff0c;RE&#xff09;是一种字符模式&#xff0c;用于在查找过程中匹配指定的字符。在大多数程序里&#xff0c;正则表达式都被置于两个正斜杠之间&#xff1b;例如/l[oO]ve/就是由正斜杠界定的正则表达式&am…

python基于ModBusTCP服务端的业务实现特定的client

python实现ModBusTCP协议的client是一件简单的事情&#xff0c;只要通过pymodbus、pyModbusTCP等模块都可以实现&#xff0c;本文采用pymodbus。但要基于ModBusTCP服务端的业务实现特定的client&#xff0c;那得看看服务端是否复杂。前面系列文章&#xff0c;我们学习了对服务端…

无需公网IP联机Minecraft,我的世界服务器本地搭建教程

目录 前言 1.Mcsmanager安装 2.创建Minecraft服务器 3.本地测试联机 4. 内网穿透 4.1 安装cpolar内网穿透 4.2 创建隧道映射内网端口 5.远程联机测试 6. 配置固定远程联机端口地址 6.1 保留一个固定TCP地址 6.2 配置固定TCP地址 7. 使用固定公网地址远程联机 8.总…

开关量防抖滤波器(梯形图和SCL源代码)

模拟量防抖超限报警功能块请查看下面文章链接: https://rxxw-control.blog.csdn.net/article/details/133969425https://rxxw-control.blog.csdn.net/article/details/133969425 1、开关量防抖滤波器 2、防抖滤波 3、梯形图代码

【conda】利用Conda创建虚拟环境,Pytorch各版本安装教程(Ubuntu)

TOC conda 系列&#xff1a; 1. conda指令教程 2. 利用Conda创建虚拟环境&#xff0c;安装Pytorch各版本教程(Ubuntu) 1. 利用Conda创建虚拟环境 nolonolo:~/sun/SplaTAM$ conda create -n splatam python3.10查看结果&#xff1a; (splatam) nolonolo:~/sun/SplaTAM$ cond…