SQL进阶之CASE表达式

CASE 表达式

越前须知(雾)

  • 本系列参考《SQL进阶教程》1,DBMS选用MySQL。
  • 本系列不涉及数据库安装与基础语句,对初学者存在一定门槛;基础知识建议阅读《SQL必知必会(第四版)》与《SQL基础教程(第二版)》。

CASE表达式概述

  • 基本写法
-- 简单表达式
case sex
	when '1' then '男'
	when '2' then '女'
	else '其他' end

-- 搜索表达式
case when sex = '1' then '男'
	 when sex = '2' then '女'
     else '其他' end
  • 注意事项
  1. 各条件分支返回数据类型须一致
  2. 记得写 ELSE 和 END

具体应用

下面按照 Q&A 形式提供解法。

转化已有编号方式并统计

A. 汇总原有编号
  • 创表
CREATE TABLE PopTbl
(pref_name VARCHAR(32) PRIMARY KEY,
 population INTEGER NOT NULL);

INSERT INTO PopTbl VALUES('德岛', 100);
INSERT INTO PopTbl VALUES('香川', 200);
INSERT INTO PopTbl VALUES('爱媛', 150);
INSERT INTO PopTbl VALUES('高知', 200);
INSERT INTO PopTbl VALUES('福冈', 300);
INSERT INTO PopTbl VALUES('佐贺', 100);
INSERT INTO PopTbl VALUES('长崎', 200);
INSERT INTO PopTbl VALUES('东京', 400);
INSERT INTO PopTbl VALUES('群马', 50);
  1. 文本对应
    Q:将“北海道、青森”各地名按照高一级的地区单位分组
    A:用case 表达式分组
select case pref_name            
		    when '德岛' then '四国'            
		    when '香川' then '四国'            
		    when '爱媛' then '四国'            
		    when '高知' then '四国'            
		    when '福冈' then '九州'            
		    when '佐贺' then '九州'            
		    when '长崎' then '九州'            
		    else '其他' end as district,     
	   sum(population) as sum_pop  
	from PopTbl    
	group by case pref_name   
	-- group by需要将整个case表达式复制到这里,原则上不能使用 district 别名,因为 group by 执行顺序先于 select
	-- 但MySQL执行前先扫描 select,可使用 group by district
			 when '德岛' then '四国'          
			 when '香川' then '四国'          
			 when '爱媛' then '四国'          
			 when '高知' then '四国'          
			 when '福冈' then '九州'          
			 when '佐贺' then '九州'          
			 when '长崎' then '九州'          
			 else '其他' end;
  1. 数字区间对应
    Q:按照人口将城市划分等级
    A:用case表达式划分数字区间
select case when population < 100 then '01'
			when population >= 100 and population < 200 then '02'
		    when population >= 200 and population < 300 then '03'
            when population >= 300 then '04'
	   		else null end as pop_class,
	   sum(population) as sum_pop
	from PopTbl
    group by pop_class
	order by pop_class;
B. 行列转换
  • 创表
CREATE TABLE PopTbl2
(pref_name VARCHAR(32),
 sex CHAR(1) NOT NULL,
 population INTEGER NOT NULL,
    PRIMARY KEY(pref_name, sex));

INSERT INTO PopTbl2 VALUES('德岛', '1',	60 );
INSERT INTO PopTbl2 VALUES('德岛', '2',	40 );
INSERT INTO PopTbl2 VALUES('香川', '1',	100);
INSERT INTO PopTbl2 VALUES('香川', '2',	100);
INSERT INTO PopTbl2 VALUES('爱媛', '1',	100);
INSERT INTO PopTbl2 VALUES('爱媛', '2',	50 );
INSERT INTO PopTbl2 VALUES('高知', '1',	100);
INSERT INTO PopTbl2 VALUES('高知', '2',	100);
INSERT INTO PopTbl2 VALUES('福冈', '1',	100);
INSERT INTO PopTbl2 VALUES('福冈', '2',	200);
INSERT INTO PopTbl2 VALUES('佐贺', '1',	20 );
INSERT INTO PopTbl2 VALUES('佐贺', '2',	80 );
INSERT INTO PopTbl2 VALUES('长崎', '1',	125);
INSERT INTO PopTbl2 VALUES('长崎', '2',	125);
INSERT INTO PopTbl2 VALUES('东京', '1',	250);
INSERT INTO PopTbl2 VALUES('东京', '2',	150);  

Q:对各地区不同性别人口进行汇总
A:select子句中用case表达式分列

select pref_name,
	   sum(case when sex = '1' then population else null end) as '男',
       sum(case when sex = '2' then population else null end) as '女'
    from PopTbl2
    group by pref_name; -- 使用了聚合函数,需要对pref_name分组

Q:生成以下表格
A:select子句中用case表达式分列
行列转换示例2

select sex as '性别', sum(population) as '全国',
	   sum(case when pref_name = '德岛' then population else null end) as '德岛',
       sum(case when pref_name = '香川' then population else null end) as '香川',
       sum(case when pref_name = '爱媛' then population else null end) as '爱媛',
       sum(case when pref_name = '高知' then population else null end) as '高知',
       sum(case when pref_name in ('德岛','香川','爱媛','高知') 
             	then population else null end) as '四国汇总'
	from PopTbl2
    group by sex;  -- 对除聚合函数以外所有字段进行分组

用CHECK约束定义多个列的条件关系

  • 创表
    Q:限制女性员工工资不超过20万
    A:在创表时加入限制
create table TestSal
	(sex char(1) not null,
     salary integer,
	 constraint check_salary check    
	 -- check_salary为约束名称,check = 1,可插入这条数据
				(case when sex = '2' 
                      then (case when salary <= 200000 then 1 else 0 end)
				      else 1 end = 1));
                  
INSERT INTO TestSal VALUES(1, 200000);
INSERT INTO TestSal VALUES(1, 300000);
INSERT INTO TestSal VALUES(1, NULL);
INSERT INTO TestSal VALUES(2, 200000);
INSERT INTO TestSal VALUES(2, 300000);  -- error
INSERT INTO TestSal VALUES(2, NULL);
INSERT INTO TestSal VALUES(1, 300000);

UPDATE子句的条件分支

  • 创表
CREATE TABLE Salaries
(name VARCHAR(32) PRIMARY KEY,
 salary INTEGER NOT NULL);

INSERT INTO Salaries VALUES('相田', 300000);
INSERT INTO Salaries VALUES('神崎', 270000);
INSERT INTO Salaries VALUES('木村', 220000);
INSERT INTO Salaries VALUES('齐藤', 290000);

Q:对不同区间的工资进行不同更新操作:30万以上降薪10%、25-28万加薪20%、其余保持不变
A: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;

表间匹配

  • 创表
CREATE TABLE CourseMaster
(course_id   INTEGER PRIMARY KEY,
 course_name VARCHAR(32) NOT NULL);

INSERT INTO CourseMaster VALUES(1, '会计入门');
INSERT INTO CourseMaster VALUES(2, '财务知识');
INSERT INTO CourseMaster VALUES(3, '簿记考试');
INSERT INTO CourseMaster VALUES(4, '税务师');

CREATE TABLE OpenCourses
(month       INTEGER ,
 course_id   INTEGER ,
    PRIMARY KEY(month, course_id));

INSERT INTO OpenCourses VALUES(200706, 1);
INSERT INTO OpenCourses VALUES(200706, 3);
INSERT INTO OpenCourses VALUES(200706, 4);
INSERT INTO OpenCourses VALUES(200707, 4);
INSERT INTO OpenCourses VALUES(200708, 2);
INSERT INTO OpenCourses VALUES(200708, 4);

Q:生成以下数据表。
表间匹配示例1
两种解法:

  1. IN
select course_name,
	   case when course_id in (select course_id from OpenCourses 
	   								where month = '200706') then '〇' 
	   		else 'X' end as '6月',
       case when course_id in (select course_id from OpenCourses 
       								where month = '200707') then '〇' 
       		else 'X' end as '7月',
       case when course_id in (select course_id from OpenCourses 
               						where month = '200708') then '〇' 
            else 'X' end as '8月'
	from CourseMaster;
  1. EXISTS
select CM.course_name, 
	   case when exists (select O.course_id from OpenCourses as O 
                         	  where month = '200706' 
                         	  and CM.course_id = O.course_id) then '〇' 
            else 'X' end as '6月',
	   case when exists (select O.course_id from OpenCourses as O 
                              where month = '200707' 
                              and CM.course_id = O.course_id) then '〇' 
            else 'X' end as '7月',
	   case when exists (select O.course_id from OpenCourses as O 
                         	  where month = '200708' 
                         	  and CM.course_id = O.course_id) then '〇' 
            else 'X' end as '8月'
	from CourseMaster as CM;

聚合函数

  • 创表
CREATE TABLE StudentClub
(std_id  INTEGER,
 club_id INTEGER,
 club_name VARCHAR(32),
 main_club_flg CHAR(1),
 PRIMARY KEY (std_id, club_id));

INSERT INTO StudentClub VALUES(100, 1, '棒球',        'Y');
INSERT INTO StudentClub VALUES(100, 2, '管弦乐',      'N');
INSERT INTO StudentClub VALUES(200, 2, '管弦乐',      'N');
INSERT INTO StudentClub VALUES(200, 3, '羽毛球',			 'Y');
INSERT INTO StudentClub VALUES(200, 4, '足球',    	  'N');
INSERT INTO StudentClub VALUES(300, 4, '足球',    		'N');
INSERT INTO StudentClub VALUES(400, 5, '游泳',        'N');
INSERT INTO StudentClub VALUES(500, 6, '围棋',        'N');

Q:查询每个学生的单一社团或多社团的主社团
A:单一社团即count(*)=1;多社团的主社团即代号为‘Y’

select std_id, 
	   case when count(*) = 1
			then max(club_id)  -- 都要加max(),保证同一列数据类型统一
            else max(case when main_club_flg = 'Y' 
                          then club_id 
                          else null end) end as main_club
	   from StudentClub
     group by std_id;

比较不同列,取最大值

  • 创表
CREATE TABLE Greatests
(key1 CHAR(1) PRIMARY KEY,
	x   INTEGER NOT NULL,
	y   INTEGER NOT NULL,
	z   INTEGER NOT NULL);

INSERT INTO Greatests VALUES('A', 1, 2, 3);
INSERT INTO Greatests VALUES('B', 5, 5, 2);
INSERT INTO Greatests VALUES('C', 4, 7, 1);
INSERT INTO Greatests VALUES('D', 3, 3, 8); 

Q:横向比较统一行不同属性列,取最大值;如A(1,2,3)取最大值3
A:两种解法

  1. MySQL特有 Greatest() 函数
select key1, greatest(greatest(x,y),z) from Greatests; 
  1. CASE表达式
select key1, 
	   case when (case when x < y then y else x end) < z then z
				  	   else (case when x < y then y 
				  	   			  else x end) end as greatest
	from Greatests;

Q:将结果按照一定顺序排序
A:order by case

select key1, 
	   case when (case when x < y then y else x end) < z then z 
	   				   else (case when x < y then y 
	   				   			  else x end) end as greatest
	from Greatests
    order by case key1 when 'B' then '1'
                       when 'A' then '2'
                       when 'D' then '3'
                       else '4' end; 

  1. MICK[日] 《SQL进阶教程》 ↩︎

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值