MySQL之case...when...then...end的详细使用
作者:mmseoamin日期:2023-12-18

目录

    • 一、简介
    • 二、简单Case函数
      • 2.1、语法定义
      • 2.2、简单函数形式
      • 三、Case搜索函数
        • 3.1、语法定义
        • 3.2、简单用法
        • 3.3、分组
        • 3.4、分组+计数
        • 3.5、分组+汇总
        • 3.6、更新语句
        • 3.7、子查询
        • 结语

          一、简介

            今天我们主要是讲讲case…when…then…end的用法,它主要分成两类:

          • 简单Case函数
          • Case搜索函数

            假设我们数据库有一个员工信息表表如下:

            CREATE TABLE `tb_employee` (
              `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
              `emp_code` int unsigned NOT NULL DEFAULT '0' COMMENT '员工编码',
              `emp_name` varchar(20) NOT NULL DEFAULT '' COMMENT '员工姓名',
              `gender` char(1) NOT NULL DEFAULT '1' COMMENT '性别(1:男0:女)',
              `dep_code` int NOT NULL DEFAULT '0' COMMENT '部门',
              `job` varchar(20) NOT NULL DEFAULT '' COMMENT '工作',
              `age` tinyint NOT NULL DEFAULT '0' COMMENT '年龄',
              `salary` double(8,2) NOT NULL DEFAULT '0.00' COMMENT '工资',
              `hire_date` date DEFAULT NULL COMMENT '入职时间',
              `manage_code` int DEFAULT NULL COMMENT '所属领导',
              `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
              `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
              PRIMARY KEY (`id`),
              KEY `idx_emp_code` (`emp_code`),
              KEY `idx_manage_code` (`manage_code`)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工信息表';
            

            接下来希望你看文章时不要因为sql长而害怕,都是些重复的东西而已,请放宽心态看待。

            二、简单Case函数

            2.1、语法定义

              语法如下:

            CASE '字段名' 
            	 WHEN '字段值1' THEN '结果1' 
                 WHEN '字段值2' THEN '结果2'
                 WHEN '字段值3' THEN '结果3'
                 ELSE '其他结果'
            END 
            

               字段名 就是数据库表中字段, 字段值 就是这个字段存储的值, 结果 就是你希望得到的结果。

            2.2、简单函数形式

              比如我们要 查询一份基本的员工信息 ,数据库里存储的是1或者0,我们肯定不会显示1或者0,而是对应的性别(男或者女),这样更加的直观。从上面 tb_student 表的定义我们知道字段 gender ,1表示男,0表示女,默认值是1,这个时候我们就可以利用 case…when…then…end 来实现

            SELECT 
            	emp_code AS '员工编号',
            	emp_name AS '员工姓名',
            	(CASE gender
            		WHEN 1 THEN '男'
            	    WHEN 0 THEN '女'
            	    ELSE '未知'
            	END) AS '性别',
            	salary AS '工资'
            FROM tb_employee;
            

              还有些人觉得 else 可以不要,但是不建议这样做,假设数据库没有设置默认值,程序又没有设置值,那就变成空了,又或者有个傻瓜蛋把 gender 的值改成了2呢?毕竟 else 是你的一个兜底。尤其是在一些字段可能会扩展的类型的时候, else 就显得很重要了。

              一般会把 case end 用括号包括,这样也便于解读或者使用别名等。

            三、Case搜索函数

            3.1、语法定义

              语法如下:

            CASE WHEN '表达式1' THEN '结果1' 
                 WHEN '表达式2' THEN '结果2'
                 WHEN '表达式3' THEN '结果3'
                 ELSE '其他结果'
            END
            

               字段名 就是数据库表中字段, 字段值 就是这个字段存储的值, 结果 就是你希望得到的结果。在Case函数中,表达式可以使用 BETWEEN,LIKE,IS NULL,IN,EXISTS 等等

            3.2、简单用法

              比如我们还是用 查询一份基本的员工信息 举例看基本使用。

            SELECT 
            	emp_code AS '员工编号',
            	emp_name AS '员工姓名',
            	(CASE 
            		WHEN gender=1 THEN '男'
            	    WHEN gender=0 THEN '女'
            	    ELSE '未知'
            	END) AS '性别',
            	salary AS '工资'
            FROM tb_employee;
            

              这样你会发现和上面简单Case函数形式差别很小,确实,如果只是等值表达式,区别很小,并且简单表达式还简单些。这里这么写只是先混个脸熟,根本没有把表达式的作用发挥出来。

            3.3、分组

              老板想看看公司里员工的薪资架构是否合理, 需要提供一份明细,查询每个人对应的级别 ,级别规划如下:

            工资范围工资级别
            员工工资小于3000的 入门
            员工工资大于等于3000并且小于15000的 初级
            员工工资大于等于15000并且小于25000的 中级
            员工工资大于等于25000并且小于50000的 高级
            员工工资大于等于50000 特级

            则我们可以使用 case…when…then…end 这一语法完成这个查询。

             SELECT 
            	emp_code AS '员工编号',
            	emp_name AS '员工姓名',
                salary AS '员工工资',
            	(CASE 
            		WHEN salary < 3000 THEN '入门级'
            		WHEN salary >= 3000 AND salary < 15000 THEN '初级'
                    WHEN salary >= 15000 AND salary < 25000 THEN '中级'
            	    WHEN salary >= 25000 AND salary < 50000 THEN '高级'
            	    ELSE '特级'
            	END) AS '工资级别'
            FROM tb_employee;
            

              这里的表达式,使用了算术表达式,and表达式,还要between…and 表达式,这里只是告诉大家可以用,实际没必要混着用。

            3.4、分组+计数

              老板想看看 公司对应的每个工资级别分别有多少人

            SELECT 
            	(CASE 
            		WHEN salary < 3000 THEN '入门级'
            		WHEN salary >= 3000 AND salary < 15000 THEN '初级'
                    WHEN salary >= 15000 AND salary < 25000 THEN '中级'
            	    WHEN salary >= 25000 AND salary < 50000 THEN '高级'
            	    ELSE '特级'
            	END) as 'levels',
                count(*) AS '总人数'
            FROM tb_employee
            GROUP BY levels;
            

              如果老板还想 细分到每个部门,及每个部门对应工资级别的总人数 ,假设部门编号从10到14分别对应则:

            编号部门
            10总经办
            11财务
            12技术
            13测试
            14运维

              那么我们只需要先按部门分组,再按工资级别分组即可

            SELECT 
            	dep_code AS '部门编号',
            	(CASE 
            		WHEN dep_code=10 THEN '总经办'
            		WHEN dep_code=11 THEN '财务'
            		WHEN dep_code=12 THEN '技术'
            		WHEN dep_code=13 THEN '测试'
            		WHEN dep_code=14 THEN '运维'
            		ELSE '其他'
            		END) AS '部门',
            	(CASE 
            		WHEN salary < 3000 THEN '入门级'
            		WHEN salary >= 3000 AND salary < 15000 THEN '初级'
                    WHEN salary >= 15000 AND salary < 25000 THEN '中级'
            	    WHEN salary >= 25000 AND salary < 50000 THEN '高级'
            	    ELSE '特级'
            	END) AS 'levels',
                count(*) as '总人数'
            FROM tb_employee
            GROUP BY dep_code,levels;
            

              实际中对应部门名称肯定是以连表查询居多,我这里是为了演示,顺便加深 case…when…then…end 用法的使用

            3.5、分组+汇总

              如果老板现在想知道, 每个部门的总工资,及每个部门中每个工资级别每个月总工资是多少 。小伙伴们想到的可能是先按部门分组,再按性别分组,然后再汇总。如果是一条记录显示这个结果,我相信很多小伙伴也不知道怎么去查询。

              我们不着急,我们先查个简单的,查询每个部门的男生总数和女生总数,以及部门的总人数。那么 case…when…then…end 的作用又来了。

            SELECT 
            	dep_code AS '部门编号',
            	(CASE 
            		WHEN dep_code=10 THEN '总经办'
            		WHEN dep_code=11 THEN '财务'
            		WHEN dep_code=12 THEN '技术'
            		WHEN dep_code=13 THEN '测试'
            		WHEN dep_code=14 THEN '运维'
            		ELSE '其他'
            	END) AS '部门',
                SUM((CASE WHEN gender = 1 THEN 1 ELSE 0 END)) AS '男生人数',
                SUM((CASE WHEN gender = 0 THEN 1 ELSE 0 END)) AS '女生人数',
                COUNT(*) AS '部门总人数'
            FROM
                tb_employee
            GROUP BY dep_code;
            

              也许即算看了代码,也许还是有不理解的,为什么两个总数在一行。

            • count(*)按部门分组,同一个部门的每一条记录都会加入结果集
            • case…when…then…end这个是同一个部门中,只有满足条件才会记录到结果集,我们这里满足就记为1,不满足,记为0,然后使用sum函数汇总

                了解了上面这个后,我们之前那个需求 每个部门的总工资,及每个部门中每个工资级别每个月总工资是多少 就容易理解了,查询如下:

              SELECT 
                  dep_code AS '部门编号',
              	(CASE 
              		WHEN dep_code=10 THEN '总经办'
              		WHEN dep_code=11 THEN '财务'
              		WHEN dep_code=12 THEN '技术'
              		WHEN dep_code=13 THEN '测试'
              		WHEN dep_code=14 THEN '运维'
              		ELSE '其他'
              	END) AS '部门',
              	SUM(salary) AS '总工资',
                  SUM((CASE WHEN salary <= 3000 THEN salary ELSE 0 END)) AS '入门总工资',
                  SUM((CASE WHEN salary > 3000 AND salary < 15000 THEN salary ELSE 0 END)) AS '初级总工资',
                  SUM((CASE WHEN salary >= 15000 AND salary < 25000 THEN salary ELSE 0 END)) AS '中级总工资',
                  SUM((CASE WHEN salary >= 25000 AND salary <= 50000 THEN salary ELSE 0 END)) AS '高级总工资',
                  SUM((CASE WHEN salary > 50000 THEN salary ELSE 0 END)) AS '特级总工资'
              FROM
                  tb_employee
              GROUP BY dep_code;
              

                其实还算可以更详细 每个部门的总人数,总工资,及每个部门中每个工资级别的人数及每个级别对应的总工资是多少

              SELECT 
                  dep_code AS '部门编号',
              	(CASE 
              		WHEN dep_code=10 THEN '总经办'
              		WHEN dep_code=11 THEN '财务'
              		WHEN dep_code=12 THEN '技术'
              		WHEN dep_code=13 THEN '测试'
              		WHEN dep_code=14 THEN '运维'
              		ELSE '其他'
              	END) AS '部门',
              	COUNT(*) AS '总人数',
              	SUM(salary) AS '总工资',
                  SUM((CASE WHEN salary <= 3000 THEN 1 ELSE 0 END)) AS '入门总人数',
                  SUM((CASE WHEN salary > 3000 AND salary < 15000 THEN 1 ELSE 0 END)) AS '初级总人数',
                  SUM((CASE WHEN salary >= 15000 AND salary < 25000 THEN 1 ELSE 0 END)) AS '中级总人数',
                  SUM((CASE WHEN salary >= 25000 AND salary <= 50000 THEN 1 ELSE 0 END)) AS '高级总人数',
                  SUM((CASE WHEN salary > 50000 THEN 1 ELSE 0 END)) AS '特级总人数',
                  SUM((CASE WHEN salary <= 3000 THEN salary ELSE 0 END)) AS '入门总工资',
                  SUM((CASE WHEN salary > 3000 AND salary < 15000 THEN salary ELSE 0 END)) AS '初级总工资',
                  SUM((CASE WHEN salary >= 15000 AND salary < 25000 THEN salary ELSE 0 END)) AS '中级总工资',
                  SUM((CASE WHEN salary >= 25000 AND salary <= 50000 THEN salary ELSE 0 END)) AS '高级总工资',
                  SUM((CASE WHEN salary > 50000 THEN salary ELSE 0 END)) AS '特级总工资'
              FROM
                  tb_employee
              GROUP BY dep_code;
              

                相当于两个例子合并了,还可以计算平均工资等就不一一列举了。

              3.6、更新语句

                公司部门编号从10到20,公司对员工的工资进行调整,除去部门10以外

              工资范围工资级别
              员工工资小于3000的 涨薪400
              员工工资大于等于3000并且小于15000的 涨薪20%
              员工工资大于等于15000并且小于25000的 涨薪10%
              员工工资大于等于25000并且小于50000的 不变
              员工工资大于等于50000 降薪10%
              UPDATE tb_employee 
              SET 
                  salary = (CASE
                      WHEN salary <= 3000 THEN salary + 400
                      WHEN salary > 3000 AND salary <= 15000 THEN salary * 1.2
                      WHEN salary > 15000 AND salary < 25000 THEN salary * 1.1
                      WHEN salary > 50000 THEN salary * 0.9
                      ELSE salary
                  END)
              where dep_code > 10;
              

              3.7、子查询

                比如对账时有本地记录 tb_local_record 和外部记录 tb_outside_record ,通过查询看哪些本地记录没有对应的外部记录。

              SELECT 
              	tranSeq as '交易流水', 
              	(CASE 
              		WHEN tranSeq IN (SELECT tranSeq FROM tb_outside_record) THEN '匹配' 
              		ELSE '未匹配' 
              	END) as '是否匹配' 
              FROM tb_local_record; 
              

              或者

              SELECT 
              	lr.tranSeq as '交易流水', 
              	(CASE 
              		WHEN EXISTS (SELECT osr.tranSeq FROM tb_outside_record osr 
              					WHERE  osr.tranSeq = lr.tranSeq) THEN '匹配' 
              		ELSE '未匹配' 
              	END) as '是否匹配' 
              FROM tb_local_record lr; 
              

              结语

                case…when…then…end的用法还有很多,比如还能联合count函数,但是一般有以上的方式,基本上就够你工作所需了。