SQL进阶之CASE表达式

    科技2024-04-14  91

    SQL进阶之CASE表达式

    CASE 表达式越前须知(雾)CASE表达式概述具体应用转化已有编号方式并统计A. 汇总原有编号B. 行列转换 用CHECK约束定义多个列的条件关系UPDATE子句的条件分支表间匹配聚合函数比较不同列,取最大值

    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 注意事项 各条件分支返回数据类型须一致记得写 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); 文本对应 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; 数字区间对应 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表达式分列

    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:生成以下数据表。 两种解法:

    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; 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:两种解法

    MySQL特有 Greatest() 函数 select key1, greatest(greatest(x,y),z) from Greatests; 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;

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

    Processed: 0.016, SQL: 8