在SQL里表达条件分支-CASE
CASE表达式是从SQL-92标准开始被引入的。在CASE 表达式里,可以使用 BETWEEN 、LIKE和 < 、> 等便利的谓词组合,以及能嵌套子查询的IN 和 EXISTS谓词。可以实现行列转换、已有数据重分组(分类)、与约束的结合使用、针对聚合结果的条件分支等功能。
简化版:DECODE(Oracle)、IF(Mysql)
1、CASE 表达式写法
简单CASE表达式
CASE sex
when '1' then '男'
when '2' then '女'
ELSE '其他' END
搜索CASE表达式
CASE
when sex = '1' then '男'
when sex = '2' then '女'
ELSE '其他' END
这两种写法的执行结果是相同的,sex 列(字段)如果是 ‘1’ ,那么结果为男;如果是 ‘2’ ,那么结果为女。
CASE在匹配给定条件时,发现为真的 WHEN 子句时,CASE 表达式的真假值判断就会中止,而剩余的 WHEN 子句会被忽略。为了避免引起不必要的混乱,使用 WHEN 子句时要注意条件的排他性。
需注意事项:
1)统一各分支返回的数据类型:一定要注意 CASE 表达式里各个分支返回的数据类型是否一致。某个分支返回字符型,而其他分支返回数值型的写法是不正确的。
2)不要忘了写 END:不写END是语法错误,这是不允许的。
3)养成写 ELSE 子句的习惯:与 END 不同,ELSE 子句是可选的,不写也不会出错。不写 ELSE 子句时,CASE 表达式的执行结果是 NULL 。
2、将已有编号方式转换为新的方式并统计(合并统计)
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
CASE pref_name
WHEN '德岛' THEN '四国'
WHEN '香川' THEN '四国'
WHEN '爱媛' THEN '四国'
WHEN '高知' THEN '四国'
WHEN '福冈' THEN '九州'
WHEN '佐贺' THEN '九州'
WHEN '长崎' THEN '九州'
ELSE '其他' END;
-- 使用select中的别名,pg、mysql中支持以下写法,Oracle、DB2、SQL Server可能会报错。
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 district;
3、用一条SQL语句进行多条件统计
SELECT
pref_name,
-- 男性人口
SUM( CASE WHEN sex=1 THEN population ELSE 0 END ) AS '男'
-- 女性人口
SUM( CASE WHEN sex=2 THEN population ELSE 0 END ) AS '女'
FROM poptlb
GROUP By pref_name;
上面代码所做的是,分别统计每个县的男性(即’1’)人口和女性(即’2’)人口。也就是说,这里是将“行结构”的数据转换成了“列结构”的数据。除了SUM,COUNT、AVG等聚合韩素也都可以用于将行结构的数据转换成列结构的数据。
4、用CHECK约束定义多个列的条件关系
假设某公司规定“女性员工的工资必须在 20 万日元以下”,而在这个公司的人事表中,这条无理的规定是使用 CHECK 约束来描述的,代码如下所示:
CONSTRAINT check_salary CHECK (
CASE WHEN sex = '2' THEN
CASE WHEN salary <= 200000
THEN 1 ELSE 0 END
ELSE 1 END = 1
)
这段代码里,CASE表达式被嵌入CHECK约束里,描述了“如果是女性员工则工资必须在 20 万日元以下”这个命题(判断事情的语句)。
5、在UPDATE语句中进行条件分支
条件:
对当前工资为 30 万日元以上的员工,降薪 10%。
对当前工资为 25 万日元以上且不满 28 万日元的员工,加薪 20%。
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;
6、表之间的数据匹配
与DECODE函数等相比,CASE的一大优势在于能判断表达式。在CASE表达式里,我们可以使用 BETWEEN 、LIKE和 < 、> 等便利的谓词组合,以及能嵌套子查询的IN 和 EXISTS谓词。
--- 使用EXIST谓词
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;
无论使用IN还是EXISTS,得到的结果是一样的,但从性能方面来说,EXISTS更好。通过EXISTS进行的子查询能够用到主键索引,因此当表中数据多时,使用EXISTS的优势会更大。
7、在CASE表达式中使用聚合函数
对于加入了多个社团的学生,通过将其“主社团标志”列设置为 Y 或者 N 来表明哪一个社团是他的主社团;对于只加入了一个社团的学生,将其“主社团标志”列设置为 N。
现需要查询出所有学生加入的社团,若加入了多个则显示主社团
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 student_club
GROUP BY std_id
8、练习
多列数据的最大值
--求x和y和z三者中的最大值
select key,
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 agreatest
from greatests;
--转换成行格式后使用MAX函数
select key,max(col) as greatest
from (select key,x as col from greatests
union all
select key,y as col form greatests
union all
select key,z as col from greatests)tmp
group by key;
-- 使用函数
select key,greatest(greatest(x,y),z) as greatest from greatests;
转换行列-在表头里加入汇总
select sex,sum(population) as total,
sum(case when pref_name = '德岛' then population else 0 end ) as col_1,
sum(case when pref_name = '香川' then population else 0 end ) as col_2,
sum(case when pref_name = '爱媛' then population else 0 end ) as col_3,
sum(case when pref_name = '高知' then population else 0 end ) as col_4,
sum(case when pref_name in('德岛','香川' ,'爱媛','高知') then population else 0 end ) as col_5
from poptb12
group by sex;
用order by生成“排序”列
select key from greatests
order by case key
when 'B' then 1
when 'A' then 2
when 'D' then 3
when 'C' then 4
else null end ;
select key from greatests
order by case
when key like 'B%' then 1
when key like 'A%' then 2
when key like 'D%' then 3
when key like 'C%' then 4
else null end ;




