您的位置:首页 > 数据库

SQL进阶之CASE表达式练习

2020-07-13 05:58 127 查看

1.case表达式简单了解

分为简单case表达式和搜索case表达式

-- 简单case表达式
case sex
when '1' then '男'
when '2' then '女'
else '其他' end
--搜索case表达式
case when sex = '1' then '男'
when sex = '2' then '女'CASE
else '其他' end

注:使用case表达式需要注意条件的排他性
示例:

-- 例如,这样写的话,结果里不会出现"第二"
case when col_1 in ('a','b') then '第一',
when col_2 in ('a') then '第二'
else '其他' end

其余注意点:
①统一分支返回的数据类型需要保持一致
②记得写END
③养成写else子句的习惯(可以清楚的看到这种条件下会生成null值)

2.casedemo 使用mysql练习:

版本使用mysql5.1.73
MySql–三种注释写法
需要特别注意 – 这种注释后面要加一个空格
#DELETE FROM SeatInformation
/*DELETE FROM SeatInformation */
– DELETE FROM SeatInformation

1.需求:将已有编号方式转换为新的方式并统计
CreateTable

/* 将已有编号方式转换为新的方式并统计 */
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);

Sample

/* 把县编号转换成地区编号(1) */
SELECT CASE pref_name
WHEN '德岛' THEN '四国'
WHEN '香川' THEN '四国'
WHEN '爱媛' THEN '四国'
WHEN '高知' THEN '四国'
WHEN '福冈' THEN '九州'
WHEN '佐贺' THEN '九州'
WHEN '长崎' THEN '九州'
ELSE '其他' END AS district, -- 区分
SUM(population) -- (简单说一个小点就是使用sum()的时候注意sum和括号之间不要留空,否则报错)
FROM PopTbl
GROUP BY CASE pref_name
WHEN '德岛' THEN '四国'
WHEN '香川' THEN '四国'
WHEN '爱媛' THEN '四国'
WHEN '高知' THEN '四国'
WHEN '福冈' THEN '九州'
WHEN '佐贺' THEN '九州'
WHEN '长崎' THEN '九州'
ELSE '其他' END;
/* 按人口数量等级划分都道府县 */
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,
COUNT(*) AS cnt
FROM PopTbl
GROUP BY 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;
/* 把县编号转换成地区编号(2):将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)
FROM PopTbl
GROUP BY district;

注:图三Sample是图一Sample的简写.
在标准sql中这种简写是不规范的,SQL流程group by 的使用应该早于select,故group by 子句中引用select字句定义的别称是不允许的.在Oracle,DB2,Sql Server会报错
但也有支持这种SQL语句的数据库例如:MySQL和PostgreSQL,原因:它们在执行查询语句时,会对select子句列表进行扫描,并对列进行计算.

/* 转换行列——在表头里加入汇总和在揭 */
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 zaijie
FROM PopTbl2
GROUP BY sex;

CreateTable

/* 用一条SQL语句进行不同条件的统计 */
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);

Sample

/* 用一条SQL语句进行不同条件的统计 */
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 PopTbl2
GROUP BY pref_name;

注:如上我们将’‘行结构’‘数据变成了’‘列结构’'数据.改变表结构我们可以使用sum,count,avg聚合函数进行完成
新手用where条件进行条件分支,高手用select 子句进行条件分支

-- 男性人口
select pref_name
,sum(population)
from PopTbl2
where sex ='1'
group by pref_name;

-- 女性人口
select pref_name
,sum(population)
from PopTbl2
where sex ='2'
group by pref_name;

CreateTable

/* 表之间的数据匹配 */
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);

Sample

/* 表的匹配:使用IN谓词 */
SELECT CM.course_name,
CASE WHEN CM.course_id IN
(SELECT course_id FROM OpenCourses
WHERE month = 200706) THEN '○'
ELSE '×' END AS "6月",
CASE WHEN CM.course_id IN
(SELECT course_id FROM OpenCourses
WHERE month = 200707) THEN '○'
ELSE '×' END AS "7月",
CASE WHEN CM.course_id IN
(SELECT course_id FROM OpenCourses
WHERE month = 200708) THEN '○'
ELSE '×' END  AS "8月"
FROM CourseMaster CM;

/* 表的匹配:使用EXISTS谓词 */
SELECT CM.course_name,
CASE WHEN EXISTS
(SELECT course_id FROM OpenCourses OC
WHERE MONTH = 200706
AND CM.course_id = OC.course_id) THEN '○'
ELSE '×' END AS "6月",
CASE WHEN EXISTS
(SELECT course_id FROM OpenCourses OC
WHERE MONTH = 200707
AND CM.course_id = OC.course_id) THEN '○'
ELSE '×' END AS "7月",
CASE WHEN EXISTS
(SELECT course_id FROM OpenCourses OC
WHERE MONTH = 200708
AND CM.course_id = OC.course_id) THEN '○'
ELSE '×' END  AS "8月"

注:使用以上demo可以不使用聚合不使用排序,月份增加仅修改select 子句就可以,拓展性好
无论使用in还是exists,得到的结果都一样,但从性能方面来说exists更好.通过exists进行的子查询能够用到"month,course_id"这样的主键索引,在OpenCourses里数据较多是更有优势
拓展:
in和exists可以相互转换,但是在有null存在的情况下not in和not exists 是不能相互转换的,not in 均输出为null.要避免使用not in 用not exists代替

case进阶:在表达式中使用聚合函数
CreateTable

/* 在CASE表达式中使用聚合函数 */
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');

Sample

/* 在CASE表达式中使用聚合函数 */
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;

注:select 字句条件分支在一定条件下不仅可以代替WHERE 同样也可以代替HAVING

/*加入一个社团用having来写的话*/
select std_id max(club_id) as main_club
from StudentClub
group by std_id
HAVING count(*) =1

CreateTable

/* 员工工资信息表 */
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);

Sample

/* 用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;

CreateTable

/* 练习题1-1:多列数据的最大值 */
CREATE TABLE Greatests
(`key` 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);

Sample

/* 求x和y二者中较大的值 */
SELECT `key`,
CASE WHEN x < y THEN y
ELSE x END AS greatest
FROM Greatests;
/* 求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 greatest
FROM Greatests
/* 转换成行格式后使用MAX函数 */
SELECT `key`, MAX(col) AS GREATEST
FROM (SELECT `key`, X AS col FROM Greatests
UNION ALL
SELECT `key`, Y AS col FROM Greatests
UNION ALL
SELECT `key`, z AS col FROM Greatests) TMP
GROUP BY `key`;
/* 仅适用于Oracle和MySQL */
SELECT KEY, GREATEST(GREATEST(X,Y), z) AS GREATEST
FROM Greatests;
/* 用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`,
CASE `key`
WHEN 'B' THEN 1
WHEN 'A' THEN 2
WHEN 'D' THEN 3
WHEN 'C' THEN 4
ELSE NULL END AS sort_col
FROM Greatests
ORDER BY sort_col;

3.总结:

1.在group by子句里使用case表达式,可以灵活选择作为聚合的编号或等级.这一点在非定制化统计时能发挥巨大的力量
2.在聚合函数中使用case表达式,可轻松将行结构数据转换成列结构数据
3.聚合函数也可以嵌套在case表达式中使用
4.想比依赖于具体的数据库的函数,case表达式有更强大的表达能力和更好的可移植性
5.case作为表达式可以写在select 子句、group by 子句,where子句、order by子句里,简单来说,在能写列名和常量的地方,通常都能写case表达式

注:以上代码均手敲试验可直接cv

参考书籍:《SQL进阶教程》 | MICK

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: