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. mysql之各版本rpm包安装

    发现每次想用mysql的rpm包直接安装的时候,都会出现找不到对应的rpm包的情况,故记录一下查找过程 进入官网->downloads->community->mysql commu ...

  2. [LeetCode] 518. Coin Change 2 硬币找零 2

    You are given coins of different denominations and a total amount of money. Write a function to comp ...

  3. Bazel安装及使用入门

    Bazel [文档][https://docs.bazel.build/versions/1.1.0/bazel-overview.html] MacOS安装 brew tap bazelbuild/ ...

  4. python:对list去重

    1.set()方法 numbers = [1,7,3,2,5,6,2,3,4,1,5] new_numbers = list(set(numbers)) print new_numbers 输出 [1 ...

  5. sonar:默认的扫描规则

    https://blog.csdn.net/liumiaocn/article/details/83550309 https://note.youdao.com/ynoteshare1/index.h ...

  6. Python: ImportRequestsError: No module named 'requests'解决方法

    运行Python程序时,出现下面错误: import requests  ModuleNotFoundError: No module named ‘requests’ 原因:没有导入requests ...

  7. LeetCode 131. 分割回文串(Palindrome Partitioning)

    131. 分割回文串 131. Palindrome Partitioning 题目描述 给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串. 返回 s 所有可能的分割方案. LeetC ...

  8. redhat7.6Linux安装Oracle19C完整版教程

    首先安装配置虚拟机,见博客https://www.cnblogs.com/xuzhaoyang/p/11264563.html 然后配置IP地址,见博客https://www.cnblogs.com/ ...

  9. Linux学习-基本命令2

    安装tree命令 yum -y install tree 测试 tree /tmp [root@wyx ~]# tree /tmp/ /tmp/ ├── anaconda.log ├── hsperf ...

  10. linux根据进程名终止进程

    2017年09月25日 19:44:32 aladdin_sun 阅读数 5235   linux根据进程名终止进程 实验环境 操作系统:CentOS Linux release 7.3.1611 ( ...