1. 前言
CASE表达式是从SQL-92标准开始被引入的,可能因为他是相对比较新的技术,所以尽管使用起来非常便利,但其真正的价值却并不怎么为人所知。很多人不用它,或者用他的简版函数,例如DECODE(Oracle),IF(MySQL)等。如果能用好它,那么SQL能解决的问题就会更广泛,写法也会更加漂亮。而且,因为CASE表达式是不依赖于具体数据库的技术,所以可以提高SQL代码的可移植性。这里强烈推荐大家改用CASE表达式,特别是使用DECODE函数的Oracle用户。
2. CASE表达式概述
CASE sex
WHEN '1' THEN '男'
WHEN '2' THEN '女'
ELSE '其他' END
CASE WHEN sex='1' THEN '男'
WHEN sex='2' THEN '女'
ELSE '其他' END
注意:
- 统一各分支返回的数据类型(如果返回数据类型不同就会报错)
- 不要忘了写END
- 养成写ELSE子句的习惯
3. 将已有编号方式转换为新的编号方式并统计
pred_name(县名) | population(人口) |
---|
德岛 | 100 |
香川 | 200 |
爱暖 | 150 |
高知 | 200 |
福冈 | 300 |
佐贺 | 100 |
长崎 | 200 |
东京 | 400 |
群马 | 50 |
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;
4. 用一条SQL进行不同条件的统计

SELECT pred_name,
SUM(population)
FROM PopTbl2
WHERE sex='1'
GROUP BY pred_name;
SELECT pred_name,
SUM(population)
FROM PopTbl2
WHERE sex='2'
GROUP BY pred_name;
SELECT pred_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 pred_name;
5. 在UPDATE语句里进行条件分支
name | salary |
---|
相田 | 300000 |
神崎 | 270000 |
木村 | 220000 |
齐藤 | 290000 |
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谓词。
course_id | course_name |
---|
1 | 会计入门 |
2 | 财务知识 |
3 | 薄记考试 |
4 | 税务师 |
month | course_id |
---|
200706 | 1 |
200706 | 3 |
200706 | 4 |
200708 | 4 |
200708 | 2 |
200708 | 4 |
开设的课程
course_name | 6月 | 7月 | 8月 |
---|
会计入门 | 〇 | X | X |
---|
财务知识 | X | X | 〇 |
---|
薄记考试 | 〇 | X | X |
---|
税务师 | 〇 | 〇 | 〇 |
---|
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;
SELECT course_name,
CASE WHEN EXISTS
(SELECT course_id FROM OpenCourses OC
WHERE month = 200706
AND OC.course_id = CM.course_id) THEN '〇'
ELSE 'x' END AS "6月",
CASE WHEN EXISTS
(SELECT course_id FROM OpenCourses OC
WHERE month = 200707
AND OC.course_id = CM.course_id) THEN '〇'
ELSE 'x' END AS "7月",
CASE WHEN EXISTS
(SELECT course_id FROM OpenCourses OC
WHERE month = 200708
AND OC.course_id = CM.course_id) THEN '〇'
ELSE 'x' END AS "8月",
FROM CourseMaster CM;
7. 在CASE表达式中使用聚合函数
std_id(学号) | club_id(社团ID) | club_name(社团名) | main_club_flg(主社团标志) |
---|
100 | 1 | 棒球 | Y |
---|
100 | 2 | 管弦乐 | N |
---|
200 | 2 | 管弦乐 | N |
---|
200 | 3 | 羽毛球 | Y |
---|
200 | 4 | 足球 | N |
---|
300 | 4 | 足球 | N |
---|
400 | 5 | 游泳 | N |
---|
500 | 6 | 围棋 | N |
---|
- 获取只加入了一个社团的学生的社团ID
- 获取加入了多个社团的学生的主社团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;
std_id | main_club |
---|
100 | 1 |
200 | 3 |
300 | 4 |
400 | 5 |
500 | 6 |
本节要点:
- 在GROUP BY子句使用CASE表达式,可以灵活的选择作为聚合的单位的编号和等级。
- 在聚合函数中使用CASE表达式,可以轻松的将行结构的数据转换成列结构的数据。
- 相反,聚合函数也可以嵌套进CASE表达式里使用
- 相比依赖于具体数据库的函数,CASE表达式有更强大的表达能力和更好的可移植性。
- 正因为CASE表达式是一种表达式而不是语言,才有了这诸多优点。