C:\PostgreSQL\9.5\bin\psql.exe –U postgres -d test

写在前面

CASE表达式是从SQL-92标准引入的,强烈建议将MySQL的IF、Oracle的DECODE用CASE表达式替代

CASE表达式概述

  • 简单表达式(simple case expression)和搜索表达式(searched case expression):简单表达式写法更简单,但实现功能有限。
  • CASE表达式注意事项:
    • 统一各分支返回的数据类型
    • 不要忘了写END
    • 养成写ELSE字句的习惯

将已有编号方式转换为新的方式并统计

/* 将已有编号方式转换为新的方式并统计 */
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) */
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 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; -- 正常情况下,GROUP BY字句在SELECT之前运行,但此处不报错。

用一条SQL语句进行不同条件的统计

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

新手用WHERE字句进行条件分支,高手用SELECT字句进行条件分支

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

/* 用CHECK约束定义多个列的条件关系 */
CREATE TABLE TestSal
(sex CHAR(1) ,
salary INTEGER,
CONSTRAINT check_salary CHECK
( 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);
/* 用CHECK约束定义多个列的条件关系 */
/* 蕴含式 */
CONSTRAINT check_salary CHECK
( CASE WHEN sex = '2'
THEN CASE WHEN salary <= 200000
THEN 1 ELSE 0 END
ELSE 1 END = 1 )

SQL三值逻辑的逻辑与和蕴含式

P Q P ^ Q P Q P -> Q
T T T T T T
T F T T F F
T U U T U T
F T F F T T
F F F F F T
F U F F U T
U T U U T T
U F F U F T
U U U U U T

在UPDATE语句里进行条件分支

-- 对当前工资为30万日元以上的员工,降薪20%
-- 同时对当前工资为25万日元以上且不满28万日元的员工,加薪20%
/* 用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;
-- 如果不写ELSE则不满足CASE WHEN语句的将会被处理成NULL

表之间的数据匹配

/* 表之间的数据匹配 */
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);
/* 表的匹配:使用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月"
FROM CourseMaster CM;

在CASE表达式中使用聚合函数

/* 在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');
/* 在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;

新手用HAVING子句进行条件分支,高手用SELECT子句进行分支

小结

  • 在GROUP BY子句里使用CASE表达式,可以灵活地选择作为聚合的单位的编号和等级。适用非定制化分组统计
  • 在聚合函数中使用CASE表达式,可以轻松地将行结构转换为列结构的数据
  • 相反,聚合函数也可以嵌套进CASE表达式里使用
  • 相比依赖于具体数据库的函数,CASE表达式有更强大的表达能力和更好的移植性

练习题

/*多列数据的最大值*/
/* 练习题1-1:多列数据的最大值(练习题1-1-3也会用到) */
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); /* 求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;
/*转换行列--在表头里加入汇总和再揭*/
SELECT CASE WHEN sex = '1' THEN '男' ELSE '女' END AS 性别,
SUM(population) AS 全国,
SUM(CASE WHEN pref_name = '德岛' THEN population ELSE 0 END) AS 德岛,
SUM(CASE WHEN pref_name = '香川' THEN population ELSE 0 END) AS 香川,
SUM(CASE WHEN pref_name = '爱媛' THEN population ELSE 0 END) AS 爱媛,
SUM(CASE WHEN pref_name = '高知' THEN population ELSE 0 END) AS 高知,
SUM(CASE WHEN pref_name IN ('德岛','香川','爱媛','高知') THEN population ELSE 0 END) AS 四国再揭
FROM PopTbl2
GROUP BY 性别
ORDER BY 性别 ASC;
/*用ORDER BY生成排序列*/
SELECT *
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;

SQL进阶系列之1CASE表达式的更多相关文章

  1. SQL进阶1:case表达式的用法示例

    一:case表达式的用法 1.SQL中的case表达式的作用是用来对"某个变量"进行某种转化,通常在select字句中使用,举个例子: 不能看出,case表达式很像我们的if el ...

  2. Linq To Sql进阶系列(六)用object的动态查询与保存log篇

    动态的生成sql语句,根据不同的条件构造不同的where字句,是拼接sql 字符串的好处.而Linq的推出,是为了弥补编程中的 Data != Object 的问题.我们又该如何实现用object的动 ...

  3. SQL进阶系列之10HAVING子句又回来了

    写在前面 HAVING子句的处理对象是集合而不是记录 各队,全队点名 --各队,全体点名! CREATE TABLE Teams (member CHAR(12) NOT NULL PRIMARY K ...

  4. SQL进阶系列之11让SQL飞起来

    写在前面 SQL的性能优化是数据库使用者必须面对的重要问题,本节侧重SQL写法上的优化,SQL的性能同时还受到具体数据库的功能特点影响,这些不在本节讨论范围之内 使用高效的查询 参数是子查询时,使用E ...

  5. SQL进阶系列之7用SQL进行集合运算

    写在前面 集合论是SQL语言的根基,因为这种特性,SQL也被称为面向集合语言 导入篇:集合运算的几个注意事项 注意事项1:SQL能操作具有重复行的集合(multiset.bag),可以通过可选项ALL ...

  6. SQL进阶系列之5外连接的用法

    写在前面 SQL本身是作为一种数据提取工具而出现,使用SQL生成各种定制化报表和非定制化报表并非SQL原本用途的功能,但这并不意味着SQL无法实现这些功能. 用外连接进行行列转换(1)(行 → 列): ...

  7. SQL进阶系列之4HAVING字句的力量

    写在前面 SQL是面向集合的语言,与面向过程和面向对象语言都不一样 寻找缺失的编号 /* 寻找缺失的编号 */ CREATE TABLE SeqTbl (seq INTEGER PRIMARY KEY ...

  8. SQL进阶系列之3三值逻辑与NULL

    写在前面 普通编程语言里的布尔型只有true和false两个值,这种逻辑体系被称为二值逻辑,而SQL语言里,还有第三个值unknown,因此SQL的逻辑体系被称为三值逻辑. Why SQL存在三值逻辑 ...

  9. SQL进阶系列之12SQL编程方法

    写在前面 KISS -- keep it sweet and simple 表的设计 注意命名的意义 英文字母 + 阿拉伯数字 + 下划线"_" 属性和列 编程的方针 写注释 注意 ...

随机推荐

  1. iptables的statistic模块配置rr

    网上搜的是这样的 : iptables -A PREROUTING -t nat -d 172.17.64.8 -m statistic --mode nth --every 2 --packet 0 ...

  2. python tkinter中的事件绑定

    一个Tkinter主要跑在mainloop进程里.Events可能来自多个地方,比如按键,鼠标,或是系统事件. Tkinter提供了丰富的方法来处理这些事件.对于每一个控件Widget,你都可以为其绑 ...

  3. Xcode使用 Organizer 分析 Crash logs

    xcode下载地址 https://developer.apple.com/download/more/

  4. ActiveMQ学习教程

    一.activemq安装 官网地址:https://activemq.apache.org/ 具体安装过程不赘述,解压即安装, 二.使用 activemq有queue和topic两种模式, 我们以qu ...

  5. 【知识总结】动态 DP

    勾起了我悲伤的回忆 -- NOIP2018 316pts -- 主要思想:将 DP 过程分解为方便单点修改和一个区间合并的操作(通常类似矩阵乘法),然后用数据结构(通常为线段树)维护. 例:给定一个长 ...

  6. PHP设计模式 - 模板方法模式

    模板模式准备一个抽象类,将部分逻辑以具体方法以及具体构造形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑.不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现.先制定一个顶级 ...

  7. LeetCode 240. 搜索二维矩阵 II(Search a 2D Matrix II) 37

    240. 搜索二维矩阵 II 240. Search a 2D Matrix II 题目描述 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target.该矩阵具有以下特性 ...

  8. [转帖]keepalived工作原理和配置、使用

    keepalived工作原理和配置.使用 https://www.iteye.com/blog/aoyouzi-2288124 keepalived是什么 keepalived是集群管理中保证集群高可 ...

  9. 【C++札记】const关键字

    C++中const关键字无处不在,我这里做下汇总,作为工具文章方便翻阅. 一:修饰数据成员 修饰的成员一单定义初始化后不能再进行修改,如: const int a = 10; a =20; //重新赋 ...

  10. SQL——JOIN(连接)

    JOIN基于多个表之间的共同字段,把多个表的行结合起来. 一.INNER JOIN 关键字 INNER JOIN关键字:在表中存在至少一个匹配时返回行. 语法如下: SELECT 列名1,列名2... ...