写在前面

HAVING子句的处理对象是集合而不是记录

各队,全队点名

  1. --各队,全体点名!
  2. CREATE TABLE Teams
  3. (member CHAR(12) NOT NULL PRIMARY KEY,
  4. team_id INTEGER NOT NULL,
  5. status CHAR(8) NOT NULL);
  6. INSERT INTO Teams VALUES('乔', 1, '待命');
  7. INSERT INTO Teams VALUES('肯', 1, '出勤中');
  8. INSERT INTO Teams VALUES('米克', 1, '待命');
  9. INSERT INTO Teams VALUES('卡伦', 2, '出勤中');
  10. INSERT INTO Teams VALUES('凯斯', 2, '休息');
  11. INSERT INTO Teams VALUES('简', 3, '待命');
  12. INSERT INTO Teams VALUES('哈特', 3, '待命');
  13. INSERT INTO Teams VALUES('迪克', 3, '待命');
  14. INSERT INTO Teams VALUES('贝斯', 4, '待命');
  15. INSERT INTO Teams VALUES('阿伦', 5, '出勤中');
  16. INSERT INTO Teams VALUES('罗伯特', 5, '休息');
  17. INSERT INTO Teams VALUES('卡根', 5, '待命');
  1. -- 找出全队全员处于待命状态的队伍
  2. -- NOT EXISTS
  3. SELECT team_id,member FROM Teams AS T1
  4. WHERE NOT EXISTS (SELECT * FROM Teams AS T2 WHERE T1.team_id = T2.team_id AND status <> '待命' );
  5. -- HAVING方法
  6. SELECT team_id FROM Teams GROUP BY team_id HAVING COUNT(*) = SUM(CASE WHEN status = '待命' THEN 1 ELSE 0 END);
  7. -- 变通方法1
  8. SELECT team_id FROM Teams GROUP BY team_id HAVING MAX(status) = '待命' AND MIN(status) ='待命';
  9. -- 变通方法2
  10. SELECT team_id, CASE WHEN MAX(status) = '待命' AND MIN(status) = '待命' THEN '全都在待命' ELSE '队长!人手不够' END AS status
  11. FROM Teams
  12. GROUP BY team_id;

单重集合与多重集合

  1. --单重集合与多重集合
  2. CREATE TABLE Materials
  3. (center CHAR(12) NOT NULL,
  4. receive_date DATE NOT NULL,
  5. material CHAR(12) NOT NULL,
  6. PRIMARY KEY(center, receive_date));
  7. INSERT INTO Materials VALUES('东京' ,'2007-4-01', '锡');
  8. INSERT INTO Materials VALUES('东京' ,'2007-4-12', '锌');
  9. INSERT INTO Materials VALUES('东京' ,'2007-5-17', '铝');
  10. INSERT INTO Materials VALUES('东京' ,'2007-5-20', '锌');
  11. INSERT INTO Materials VALUES('大阪' ,'2007-4-20', '铜');
  12. INSERT INTO Materials VALUES('大阪' ,'2007-4-22', '镍');
  13. INSERT INTO Materials VALUES('大阪' ,'2007-4-29', '铅');
  14. INSERT INTO Materials VALUES('名古屋', '2007-3-15', '钛');
  15. INSERT INTO Materials VALUES('名古屋', '2007-4-01', '钢');
  16. INSERT INTO Materials VALUES('名古屋', '2007-4-24', '钢');
  17. INSERT INTO Materials VALUES('名古屋', '2007-5-02', '镁');
  18. INSERT INTO Materials VALUES('名古屋', '2007-5-10', '钛');
  19. INSERT INTO Materials VALUES('福冈' ,'2007-5-10', '锌');
  20. INSERT INTO Materials VALUES('福冈' ,'2007-5-28', '锡');
  1. -- 找出原料出现重复的产地
  2. SELECT center
  3. FROM Materials
  4. GROUP BY center
  5. HAVING COUNT(*) <> COUNT(DISTINCT material);
  6. -- 另一种写法
  7. SELECT center,CASE WHEN COUNT(*) <> COUNT(DISTINCT material) THEN '存在重复' ELSE '不存在重复' END AS status
  8. FROM Materials
  9. GROUP BY center;
  10. -- EXISTS写法
  11. SELECT center,material
  12. FROM Materials AS M1
  13. WHERE EXISTS (SELECT * FROM Materials AS M2 WHERE M1.center = M2.center AND M1.receive_date <> M2.receive_date AND M1.material = M2.material);

寻找缺失的编号:升级版

  1. --寻找缺失的编号:升级版
  2. CREATE TABLE SeqTbl
  3. ( seq INTEGER NOT NULL PRIMARY KEY);
  4. --不存在缺失编号(起始值=1
  5. DELETE FROM SeqTbl;
  6. INSERT INTO SeqTbl VALUES(1);
  7. INSERT INTO SeqTbl VALUES(2);
  8. INSERT INTO SeqTbl VALUES(3);
  9. INSERT INTO SeqTbl VALUES(4);
  10. INSERT INTO SeqTbl VALUES(5);
  1. -- 如果有查询结果,说明存在缺失的编号
  2. SELECT '存在缺失的编号' AS gap FROM SeqTbl HAVING COUNT(*) <> MAX(seq) --只适用于从1开始的序列
  3. -- 如果有查询结果,说明存在缺失的编号:只调查数列的连续性
  4. SELECT '存在缺失的编号' AS gap FROM SeqTbl HAVING COUNT(*) <> MAX(seq) - MIN(seq) + 1;
  5. -- 不论是否存在都返回一行结果
  6. SELECT CASE WHEN COUNT(*) = 0 THEN '空表'
  7. WHEN COUNT(*) = MAX(seq) - MIN(seq) + 1 THEN '不存在缺失编号'
  8. ELSE '存在缺失编号' END AS gap
  9. FROM SeqTbl;
  10. -- 查找最小的缺失编号
  11. SELECT CASE WHEN COUNT(*) = 0 OR MIN(seq) > 1 THEN 1
  12. ELSE (SELECT MIN(seq+1) FROM SeqTbl AS S1 WHERE NOT EXISTS (SELECT * FROM SeqTbl AS S2 WHERE S2.seq = S1.seq + 1)) END
  13. FROM SeqTbl;

为集合设置详细的条件

  1. --为集合设置详细的条件
  2. CREATE TABLE TestResults
  3. (student CHAR(12) NOT NULL PRIMARY KEY,
  4. class CHAR(1) NOT NULL,
  5. sex CHAR(1) NOT NULL,
  6. score INTEGER NOT NULL);
  7. INSERT INTO TestResults VALUES('001', 'A', '男', 100);
  8. INSERT INTO TestResults VALUES('002', 'A', '女', 100);
  9. INSERT INTO TestResults VALUES('003', 'A', '女', 49);
  10. INSERT INTO TestResults VALUES('004', 'A', '男', 30);
  11. INSERT INTO TestResults VALUES('005', 'B', '女', 100);
  12. INSERT INTO TestResults VALUES('006', 'B', '男', 92);
  13. INSERT INTO TestResults VALUES('007', 'B', '男', 80);
  14. INSERT INTO TestResults VALUES('008', 'B', '男', 80);
  15. INSERT INTO TestResults VALUES('009', 'B', '女', 10);
  16. INSERT INTO TestResults VALUES('010', 'C', '男', 92);
  17. INSERT INTO TestResults VALUES('011', 'C', '男', 80);
  18. INSERT INTO TestResults VALUES('012', 'C', '女', 21);
  19. INSERT INTO TestResults VALUES('013', 'D', '女', 100);
  20. INSERT INTO TestResults VALUES('014', 'D', '女', 0);
  21. INSERT INTO TestResults VALUES('015', 'D', '女', 0);
  1. -- 查出75%以上的学生分数都在80分以上的班级
  2. SELECT class
  3. FROM TestResults
  4. GROUP BY class
  5. HAVING COUNT(*) * 0.75 <= SUM(CASE WHEN score >= 80 THEN 1 ELSE 0 END);
  6. -- 查出分数在50分以上的男生比女生多的班级
  7. SELECT class
  8. FROM TestResults
  9. GROUP BY class
  10. 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);
  11. -- 比较男生和女生平均分的SQL语句(2):对空集求平均值后返回NULL
  12. SELECT class
  13. FROM TestResults
  14. GROUP BY class
  15. HAVING AVG(CASE WHEN sex = '男' THEN score ELSE NULL END) < AVG(CASE WHEN sex = '女' THEN score ELSE NULL END);

小结

常用调查集合性质的条件极其用途

No 条件表达式 用途
1 COUNT(DISTINCT col)=COUNT(col) col列没有重复值
2 COUNT(*)=COUNT(col) col列没有空值
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)) col最少有一个0
9 MIN(col-常量) = -MAX(col-常量) col列的最大值和最小值与指定常量等距
  • SQL指定搜索条件时,最重要的是搞清楚搜索的实体是集合还是集合的元素
  • 如果一个实体对应着一行数据,那么就是元素,所以使用WHERE子句
  • 如果一个实体对应着多行数据,那么就是集合,所以使用HAVING子句
  • HAVING子句 可以通过聚合函数针对集合指定各种条件
  • 如果通过CASE表达式生成特征函数,那么无论多么复杂的条件都可以描述
  • HAVING子句很强大

练习题

  1. /* 练习题1-10-1:单重集合与多重集合的一般化 */
  2. CREATE TABLE Materials2
  3. (center VARCHAR(32) NOT NULL,
  4. receive_date DATE NOT NULL,
  5. material VARCHAR(32) NOT NULL,
  6. orgland VARCHAR(32) NOT NULL,
  7. PRIMARY KEY(center, receive_date, material));
  8. INSERT INTO Materials2 VALUES('东京', '2007-04-01', '锡', '智利');
  9. INSERT INTO Materials2 VALUES('东京', '2007-04-12', '锌', '泰国');
  10. INSERT INTO Materials2 VALUES('东京', '2007-05-17', '铝', '巴西');
  11. INSERT INTO Materials2 VALUES('东京', '2007-05-20', '锌', '泰国');
  12. INSERT INTO Materials2 VALUES('大阪', '2007-04-20', '铜', '澳大利亚');
  13. INSERT INTO Materials2 VALUES('大阪', '2007-04-22', '镍', '南非');
  14. INSERT INTO Materials2 VALUES('大阪', '2007-04-29', '铅', '印度');
  15. INSERT INTO Materials2 VALUES('名古屋', '2007-03-15', '钛', '玻利维亚');
  16. INSERT INTO Materials2 VALUES('名古屋', '2007-04-01', '钢', '智利');
  17. INSERT INTO Materials2 VALUES('名古屋', '2007-04-24', '钢', '阿根廷');
  18. INSERT INTO Materials2 VALUES('名古屋', '2007-05-02', '镁', '智利');
  19. INSERT INTO Materials2 VALUES('名古屋', '2007-05-10', '钛', '泰国');
  20. INSERT INTO Materials2 VALUES('福冈', '2007-05-10', '锌', '美国');
  21. INSERT INTO Materials2 VALUES('福冈', '2007-05-28', '锡', '俄罗斯');
  1. /* 练习题1-10-1:单重集合与多重集合的一般化
  2. -- 找出产品和原产地都一样的center
  3. SELECT center
  4. FROM Materials2 AS M1
  5. WHERE EXISTS (SELECT * FROM Materials2 AS M2 WHERE M1.center = M2.center AND M1.receive_date <> M2.receive_date AND M1.material = M2.material AND M1.orgland = M2.orgland )
  6. GROUP BY center;
  1. /* 练习题1-10-1:单重集合与多重集合的一般化
  2. 选择(材料, 原产国)组合有重复的生产地 */
  3. SELECT center
  4. FROM Materials2
  5. GROUP BY center
  6. HAVING COUNT(material || orgland) <> COUNT(DISTINCT material || orgland);
  1. /* 练习题1-10-2:多个条件的特征函数 */
  2. CREATE TABLE TestScores
  3. (student_id INTEGER NOT NULL,
  4. subject VARCHAR(16) NOT NULL,
  5. score INTEGER NOT NULL,
  6. PRIMARY KEY (student_id, subject));
  7. INSERT INTO TestScores VALUES(100, '数学', 100);
  8. INSERT INTO TestScores VALUES(100, '语文', 80);
  9. INSERT INTO TestScores VALUES(100, '理化', 80);
  10. INSERT INTO TestScores VALUES(200, '数学', 80);
  11. INSERT INTO TestScores VALUES(200, '语文', 95);
  12. INSERT INTO TestScores VALUES(300, '数学', 40);
  13. INSERT INTO TestScores VALUES(300, '语文', 50);
  14. INSERT INTO TestScores VALUES(300, '社会', 55);
  15. INSERT INTO TestScores VALUES(400, '数学', 80);
  1. -- 找出数学分数在80分以上,且语文分数在50分以上的学生
  2. SELECT student_id
  3. FROM TestScores
  4. WHERE subject IN ('语文','数学')
  5. GROUP BY student_id
  6. HAVING SUM(CASE WHEN subject = '数学' AND score > 80 THEN 1
  7. WHEN subject = '语文' AND score > 50 THEN 1
  8. ELSE 0 END)=2;

SQL进阶系列之10HAVING子句又回来了的更多相关文章

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

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

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

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

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

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

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

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

  5. SQL进阶系列之8EXISTS谓词的用法

    写在前面 支撑SQL和关系数据库的基础理论:数学领域的集合论和逻辑学标准体系的谓词逻辑 理论篇 什么是谓词?谓词是返回值为真值(true false unknown)的函数 关系数据库里,每一个行数据 ...

  6. SQL进阶系列之6用关联子查询比较行与行

    写在前面 使用SQL对同一行数据进行列间的比较很简单,只需要在WHERE子句里写上比较条件就可以了,对于不同行数据进行列间比较需要使用自关联子查询. 增长.减少.维持现状 需要用到行间比较的经典场景是 ...

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

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

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

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

  9. SQL进阶系列之0窗口函数

    窗口函数 What's 窗口函数? 窗口函数也称为OLAP(OnLine Analytical Processing)函数,目前MySQL还不支持. 窗口函数的语法 <窗口函数> OVER ...

随机推荐

  1. QT+FFMPEG+SDL2.0实现视频播放

    开发环境:MinGW+QT5.9+FFMPEG20190212+SDL2.0.9 一.开发环境搭建 (1)下载工具 在https://ffmpeg.zeranoe.com/builds/下载对应版本. ...

  2. spark 读写text,csv,json,parquet

    以下代码演示的是spark读取 text,csv,json,parquet格式的file 为dataframe, 将dataframe保存为对应格式的文件 package com.jason.spar ...

  3. mysql8.0 grant 创建账号及权限记录

    针对 42000错误 原文:https://stackoverflow.com/questions/50177216/how-to-grant-all-privileges-to-root-user- ...

  4. HTML5自定义select标签样式的方法

    HTML5自定义select标签样式的方法 -webkit-appearance: none; 这个东西可以隐藏箭头 不过手机端就直接 设置透明度为0就行了(如果这种做法比前面个要麻烦点 毕竟还要对他 ...

  5. TCP/IP学习笔记1--概述,分组交换协议

    1.TCP/IP 互联网是由许多独立发展的网络通信技术融合而成的,能够使它们不断融合并实现统一的正式TCP/IP技术,TCP/IP使通信协议的统称. TCP/IP协议模型(Transmission C ...

  6. AVR单片机教程——EasyElectronics Library v1.3手册

    bit.h delay.h pin.h wave.h pwm.h tone.h adc.h button.h switch.h rotary.h pot.h ldr.h led.h rgbw.h se ...

  7. kali更新软件源

    首先就是修改软件源文件 /etc/apt/sources.list 可以用leafpad打开,在终端中键入: leafpad /etc/apt/sources.list 原码是kali官方的软件源,更 ...

  8. appium 方法整理

    1.contexts contexts(self):     Returns the contexts within the current session.     返回当前会话中的上下文,使用后可 ...

  9. .Net Core 图片上传FormData和Base64

    缓冲和流式传输是上传文件的两种常用方案,这里主要演示流式传输. 1.Net Core MVC Form提交方式: 前端页面 form表单提交: <form id="uploadForm ...

  10. dump net core lldb 安装

    原文https://www.cnblogs.com/calvinK/p/9263696.html centos7下安装lldb,dotnet netcore 进程生成转储文件,并使用lldb进行分析 ...