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.mzph.cn/news/207253.shtml

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

相关文章

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

1、reference wrapper 例如声明如下的模板&#xff1a; template <typename T> void foo(T val); 如果调用使用&#xff1a; int x; foo(std::ref(x)); T变成int&&#xff0c;而使用调用 int x; foo(std::cref(x)); T变成const int&。 这个特性被C标准库用…

fijkplayer flutter 直播流播放

fijkplayer flutter 直播流播放 fijkplayer 是 ijkplayer 的 Flutter 封装&#xff0c; 是一款支持 android 和 iOS 的 Flutter 媒体播放器插件&#xff0c; 由 ijkplayer 底层驱动。 通过纹理&#xff08;Texture&#xff09;接入播放器视频渲染到 Flutter 中。 前言 目前使用…

PostgreSQL 技术内幕(十二) CloudberryDB 并行化查询之路

随着数据驱动的应用日益增多&#xff0c;数据查询和分析的量级和时效性要求也在不断提升&#xff0c;对数据库的查询性能提出了更高的要求。为了满足这一需求&#xff0c;数据库引擎不断经历创新&#xff0c;其中并行执行引擎是性能提升的重要手段之一&#xff0c;逐渐成为数据…

One-to-Few Label Assignment for End-to-End Dense Detection阅读笔记

One-to-Few Label Assignment for End-to-End Dense Detection阅读笔记 Abstract 一对一&#xff08;o2o&#xff09;标签分配对基于变换器的端到端检测起着关键作用&#xff0c;最近已经被引入到全卷积检测器中&#xff0c;用于端到端密集检测。然而&#xff0c;o2o可能因为…

elasticsearch 内网下如何以离线的方式上传任意的huggingFace上的NLP模型(国内避坑指南)

es自2020年的8.x版本以来&#xff0c;就提供了机器学习的能力。我们可以使用es官方提供的工具eland&#xff0c;将hugging face上的NLP模型&#xff0c;上传到es集群中。利用es的机器学习模块&#xff0c;来运维部署管理模型。配合es的管道处理&#xff0c;来更加便捷的处理数据…

吴恩达《机器学习》12-1:优化目标

在机器学习的旅程中&#xff0c;我们已经接触了多种学习算法。在监督学习中&#xff0c;选择使用算法 A 还是算法 B 的重要性逐渐减弱&#xff0c;而更关键的是如何在应用这些算法时优化目标。这包括设计特征、选择正则化参数等因素&#xff0c;这些在不同水平的实践者之间可能…

UG NX二次开发(C#)-求曲线在某一点处的法矢和切矢

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1、前言2、在UG NX中创建一个曲线3、直接放代码4、测试案例1、前言 最近确实有点忙了,好久没更新博客了。今天恰好有时间,就更新下,还请家人们见谅。 今天我们讲一下如何获取一条曲线上某一条曲…

注意力机制的快速学习

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

Pytorch从零开始实战12

Pytorch从零开始实战——DenseNet算法实战 本系列来源于365天深度学习训练营 原作者K同学 文章目录 Pytorch从零开始实战——DenseNet算法实战环境准备数据集模型选择开始训练可视化总结 环境准备 本文基于Jupyter notebook&#xff0c;使用Python3.8&#xff0c;Pytorch2.…

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

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

【网络编程】-- 02 端口、通信协议

网络编程 3 端口 端口表示计算机上的一个程序的进程 不同的进程有不同的端口号&#xff01;用来区分不同的软件进程 被规定总共0~65535 TCP,UDP&#xff1a;65535 * 2 在同一协议下&#xff0c;端口号不可以冲突占用 端口分类&#xff1a; 公有端口&#xff1a;0~1023 HT…

亚信安慧AntDB数据库中级培训ACP上线,中国移动总部首批客户认证通过

近日&#xff0c;亚信安慧AntDB数据库ACP&#xff08;AntDB Certified Professional&#xff09;中级培训课程于官网上线。在中国移动总部客户运维团队、现场项目部伙伴和AntDB数据库成员的协同组织下&#xff0c;首批中级认证学员顺利完成相关课程的培训&#xff0c;并获得Ant…

自然语言处理22-基于本地知识库的快速问答系统,利用大模型的中文训练集为知识库

大家好,我是微学AI,今天给大家介绍一下自然语言处理22-基于本地知识库的快速问答系统,利用大模型的中文训练集为知识库。我们的快速问答系统是基于本地知识库和大模型的最新技术,它利用了经过训练的中文大模型,该模型使用了包括alpaca_gpt4_data的开源数据集。 一、本地…

C //例10.3 从键盘读入若干个字符串,对它们按字母大小的顺序排序,然后把排好序的字符串送到磁盘文件中保存。

C程序设计 &#xff08;第四版&#xff09; 谭浩强 例10.3 例10.3 从键盘读入若干个字符串&#xff0c;对它们按字母大小的顺序排序&#xff0c;然后把排好序的字符串送到磁盘文件中保存。 IDE工具&#xff1a;VS2010 Note: 使用不同的IDE工具可能有部分差异。 代码块 方法…

2023_Spark_实验二十五:SparkStreaming读取Kafka数据源:使用Direct方式

SparkStreaming读取Kafka数据源&#xff1a;使用Direct方式 一、前提工作 安装了zookeeper 安装了Kafka 实验环境&#xff1a;kafka zookeeper spark 实验流程 二、实验内容 实验要求&#xff1a;实现的从kafka读取实现wordcount程序 启动zookeeper zk.sh start# zk.sh…

SNMP陷阱监控工具

SNMP&#xff08;简单网络管理协议&#xff09;是网络管理的一个重要方面&#xff0c;其中网络设备&#xff08;包括路由器、交换机和服务器&#xff09;在满足预定义条件时将SNMP陷阱作为异步通知发送到中央管理系统。简而言之&#xff0c;每当发生关键服务器不可用或硬件高温…

microblaze仿真

verdivcs (1) vlogan/vcs增加编译选项 -debug_accessall -kdb -lca (2) 在 simulation 选项中加入下面三个选项 -guiverdi UVM_VERDI_TRACE"UVM_AWARERALHIERCOMPWAVE" UVM_TR_RECORD 这里 -guiverdi是启动verdi 和vcs联合仿真。UVM_VERDI_TRACE 这里是记录 U…

linux高级篇基础理论七(Tomcat)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️不能因为人生的道路坎坷,就使自己的身躯变得弯曲;不能因为生活的历程漫长,就使求索的 脚步迟缓。 ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xff1a;云计算技…

vue pc官网顶部导航栏组件

官网顶部导航分为一级导航和二级导航 导航的样子 文件的层级 router 文件层级 header 组件代码 <h1 class"logo-wrap"><router-link to"/"><img class"logo" :src"$config.company.logo" alt"" /><i…