版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_31156277/article/details/84847520
本文是《sql进阶教程》阅读笔记,感兴趣可以阅读该书对应章节,这本适合有一定sql基础的同学阅读。另外作者《sql基础教程》也值得一看
案例一、各队,全体点名
查出现在可以出勤的队伍。可以出勤即队伍里所有队员都处于“待命”
状态
Teams
member(队员) | team_id(队伍编号 ID) | status(状态) |
---|---|---|
乔 | 1 | 待命 |
肯 | 1 | 出勤中 |
米克 | 1 | 待命 |
卡伦 | 2 | 出勤中 |
凯斯 | 2 | 休息 |
简 | 3 | 待命 |
哈特 | 3 | 待命 |
迪克 | 3 | 待命 |
贝斯 | 4 | 待命 |
阿伦 | 5 | 出勤中 |
罗伯特 | 5 | 休息 |
卡根 | 5 | 待命 |
-- 用谓词表达全称量化命题
SELECT team_id, member
FROM Teams T1
WHERE NOT EXISTS
(SELECT *
FROM Teams T2
WHERE T1.team_id = T2.team_id
AND status <> '待命'
);
-- “所有队员都处于待命状态”=“不存在不处于待命状态的队员”
-- 用集合表达全称量化命题(1)
SELECT team_id
FROM Teams
GROUP BY team_id
HAVING COUNT(*) = SUM(CASE WHEN status = '待命' THEN 1 ELSE 0 END);
--如果元素最大值和最小值相等,那么这个集合中肯定只有一种值
SELECT team_id
FROM Teams
GROUP BY team_id
HAVING MAX(status) = '待命'
AND MIN(status) = '待命';
-- 列表显示各个队伍是否所有队员都在待命
SELECT team_id,
CASE WHEN MAX(status) = '待命' AND MIN(status) = '待命'
THEN '全都在待命'
ELSE '队长!人手不够' END AS status
FROM Teams
GROUP BY team_id;
二、单重集合与多重集合
允许循环插入和频繁读写的表中有可能产生重复数据。在定义表时加入唯一性约束可以预防表中产生重复数据,但是有些情况下根据具体的业务需求不同,产生重复数据也是合理的
有下面这样一张管理各个生产地的材料库存的表
Materials
center(生产地) | receive_date(入库日期) | material(材料) |
---|---|---|
东京 | 2007-4-01 | 锡 |
东京 | 2007-4-12 | 锌 |
东京 | 2007-5-17 | 铝 |
东京 | 2007-5-20 | 锌 |
大阪 | 2007-4-20 | 铜 |
大阪 | 2007-4-22 | 镍 |
大阪 | 2007-4-29 | 铅 |
名古屋 | 2007-3-15 | 钛 |
名古屋 | 2007-4-01 | 钢 |
名古屋 | 2007-4-24 | 钢 |
名古屋 | 2007-4-24 | 钢 |
名古屋 | 2007-5-02 | 镁 |
名古屋 | 2007-5-10 | 钛 |
福冈 | 2007-5-10 | 锌 |
福冈 | 2007-5-28 | 锡 |
-- 选中材料存在重复的生产地
SELECT center
FROM Materials
GROUP BY center
HAVING COUNT(material) <> COUNT(DISTINCT material);
-- 把条件移到SELECT 子句中,获取具体的名称
SELECT center,
CASE WHEN COUNT(material) <> COUNT(DISTINCT material) THEN '存在重复'
ELSE '不存在重复' END AS status
FROM Materials
GROUP BY center;
--group by 划分子集
-- 存在重复的集合:使用EXISTS
--过将 HAVING 改写成 EXISTS 的方式来解决(查询每天重复情况)
SELECT center, material
FROM Materials M1
WHERE EXISTS
(SELECT *
FROM Materials M2
WHERE M1.center = M2.center
AND M1.receive_date <> M2.receive_date
AND M1.material = M2.material
);
--用 EXISTS 改写后的 SQL 语句也能够查出重复的具体是哪一种材料;
--如果想要查出不存在重复材料的生产地有哪些,只需要把 EXISTS 改写为 NOT EXISTS 就可以了。
案例三、寻找缺失的编号:升级版
-- 如果有查询结果,说明存在缺失的编号
SELECT '存在缺失的编号' AS gap
FROM SeqTbl
HAVING COUNT(*) <> MAX(seq);
-- 如果有查询结果,说明存在缺失的编号:只调查数列的连续性
SELECT '存在缺失的编号' AS gap
FROM SeqTbl
HAVING COUNT(*) <> MAX(seq) - MIN(seq) + 1 ;
-- 不论是否存在缺失的编号都返回一行结果
SELECT CASE WHEN COUNT(*) = 0 THEN '表为空'
WHEN COUNT(*) <> MAX(seq) - MIN(seq) + 1 THEN '存在缺失的编号'
ELSE '连续' END AS gap
FROM SeqTbl;
案例四、为集合设置详细的条件
记录了学生考试成绩的表为例进行讲解
TestResults
student_id(学号 ID) | class(班级) | sex(性别) | score(分数) |
---|---|---|---|
001 | A | 男 | 100 |
002 | A | 女 | 100 |
003 | A | 女 | 49 |
004 | A | 男 | 30 |
005 | B | 女 | 100 |
006 | B | 男 | 92 |
007 | B | 男 | 80 |
008 | B | 男 | 80 |
009 | B | 女 | 10 |
010 | C | 男 | 92 |
011 | C | 男 | 80 |
012 | C | 女 | 21 |
013 | D | 女 | 100 |
014 | D | 女 | 0 |
015 | D | 女 | 0 |
问题一、请查询出 75% 以上的学生分数都在 80 分以上的班级
SELECT class
FROM TestResults
GROUP BY class
HAVING COUNT(*) * 0.75 <= SUM(CASE WHEN score >= 80 THEN 1 ELSE 0 END) ;
问题二、请查询出分数在 50 分以上的男生的人数比分数在 50 分以上的女生的人数多的班级
SELECT class
FROM TestResults
GROUP BY class
HAVING SUM(CASE WHEN score >= 50 AND sex = '男' THEN 1 ELSE 0 END)
> SUM(CASE WHEN score >= 50 AND sex = '女' THEN 1 ELSE 0 END);
问题三、请查询出女生平均分比男生平均分高的班级
-- 比较男生和女生平均分的SQL 语句(1):对空集使用AVG 后返回0
SELECT class
FROM TestResults
GROUP BY class
HAVING AVG(CASE WHEN sex = '男' THEN score ELSE 0 END)
< AVG(CASE WHEN sex = '女' THEN score ELSE 0 END) ;
注意:根据标准 SQL 的定义,对空集使用 AVG 函数时,结果会返回 NULL
小结
概括使用 HAVING 子句时的要点,就是要搞清楚将什么东西抽象成集合
集合性质的常用条件及其用途
No | 条件表达式 | 用途 |
---|---|---|
1 | COUNT (DISTINCT col) = COUNT(col) |
col 列没有重复的值 |
2 | COUNT(*) = COUNT(col) |
col 列不存在 NULL |
3 | COUNT(*) = MAX(col) |
col 列是连续的编号(起始值是 1) |
4 | COUNT(*) = MAX(col) - MIN(col) +1 |
col 列是连续的编号(起始值是任意整数) |
5 | MIN(col) = MAX(col) |
col 列都是相同值,或者是 NULL |
6 | MIN(col) * MAX(col) > 0 |
col 列全是正数或全是负数 |
7 | MIN(col) * MAX(col) < 0 |
col 列的最大值是正数,最小值是负数 |
8 | MIN(ABS(col)) = 0 |
col 列最少有一个是 0 |
9 | MIN(col - 常量 ) = - MAX(col - 常量) |
col 列的最大值和最小值与指定常量等距 |
一、如果使用 CASE 表达式来生成特征函数,那么无论多么复杂且通用的条件,我们都可以描述
二、HAVING 子句可以通过聚合函数(特别是极值函数)针对集合指定各种条件