《sql進階教程》之HAVING子句
阿新 • • 發佈:2019-01-01
本文是《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 子句可以通過聚合函式(特別是極值函式)針對集合指定各種條件