SQL進階之HAVING練習

一、HAVING子句簡介

HAVING是在sql的運行完後做的一個判斷常跟聚合寫法後面

二、HAVING實操

注:版本使用:mysql5.1.73
以下代碼均可直接cv在mysql練習

1.CreateTable

/* 尋找缺失的編號 */
CREATE TABLE SeqTbl
(seq  INTEGER PRIMARY KEY,
 name VARCHAR(16) NOT NULL);

INSERT INTO SeqTbl VALUES(1,	'迪克');
INSERT INTO SeqTbl VALUES(2,	'安');
INSERT INTO SeqTbl VALUES(3,	'萊露');
INSERT INTO SeqTbl VALUES(5,	'卡');
INSERT INTO SeqTbl VALUES(6,	'瑪麗');
INSERT INTO SeqTbl VALUES(8,	'本');

/* 用HAVING子句進行子查詢:求衆數(求中位數時也用本代碼) */
CREATE TABLE Graduates
(name   VARCHAR(16) PRIMARY KEY,
 income INTEGER NOT NULL);

INSERT INTO Graduates VALUES('桑普森', 400000);
INSERT INTO Graduates VALUES('邁克',     30000);
INSERT INTO Graduates VALUES('懷特',   20000);
INSERT INTO Graduates VALUES('阿諾德', 20000);
INSERT INTO Graduates VALUES('史密斯',     20000);
INSERT INTO Graduates VALUES('勞倫斯',   15000);
INSERT INTO Graduates VALUES('哈德遜',   15000);
INSERT INTO Graduates VALUES('肯特',     10000);
INSERT INTO Graduates VALUES('貝克',   10000);
INSERT INTO Graduates VALUES('斯科特',   10000);

/* 用關係除法運算進行購物籃分析 */
CREATE TABLE Items
(item VARCHAR(16) PRIMARY KEY);
 
CREATE TABLE ShopItems
(shop VARCHAR(16),
 item VARCHAR(16),
    PRIMARY KEY(shop, item));

INSERT INTO Items VALUES('啤酒');
INSERT INTO Items VALUES('紙尿褲');
INSERT INTO Items VALUES('自行車');

INSERT INTO ShopItems VALUES('仙台',  '啤酒');
INSERT INTO ShopItems VALUES('仙台',  '紙尿褲');
INSERT INTO ShopItems VALUES('仙台',  '自行車');
INSERT INTO ShopItems VALUES('仙台',  '窗簾');
INSERT INTO ShopItems VALUES('東京',  '啤酒');
INSERT INTO ShopItems VALUES('東京',  '紙尿褲');
INSERT INTO ShopItems VALUES('東京',  '自行車');
INSERT INTO ShopItems VALUES('大阪',  '電視');
INSERT INTO ShopItems VALUES('大阪',  '紙尿褲');
INSERT INTO ShopItems VALUES('大阪',  '自行車');

/* 查詢不包含NULL的集合 */
CREATE TABLE Students
(student_id   INTEGER PRIMARY KEY,
 dpt          VARCHAR(16) NOT NULL,
 sbmt_date    DATE);

INSERT INTO Students VALUES(100,  '理學院',   '2005-10-10');
INSERT INTO Students VALUES(101,  '理學院',   '2005-09-22');
INSERT INTO Students VALUES(102,  '文學院',   NULL);
INSERT INTO Students VALUES(103,  '文學院',   '2005-09-10');
INSERT INTO Students VALUES(200,  '文學院',   '2005-09-22');
INSERT INTO Students VALUES(201,  '工學院',   NULL);
INSERT INTO Students VALUES(202,  '經濟學院', '2005-09-25');

2.Sample

/* 需求:修改編號缺失的檢查邏輯,使結果總是返回一行數據 */
/* 寫法一:union+having*/
SELECT ' 存在缺失的編號' AS gap
  FROM SeqTbl
HAVING COUNT(*) <> MAX(seq) 
UNION ALL
SELECT ' 不存在缺少的編號' AS gap
  FROM SeqTbl
HAVING COUNT(*) = MAX(seq);

/* 寫法二:case when*/
SELECT CASE WHEN COUNT(*) <> MAX(seq)
            THEN '存在缺失的編號'
            ELSE '不存在缺失的編號' END AS gap
  FROM SeqTbl;

/*查詢缺失編號的最小值*/
SELECT MIN(seq+1) AS gap
	FROM SeqTbl
WHERE seq is not null and (seq+1) NOT IN (SELECT seq FROM SeqTbl)
注:若是seq存在空值就會出現沒有結果的情況,使用 not in 一定要注意null

/* 練習“特徵函數” 
   查找所有學生都在9月份提交完成的學院(1):使用BETWEEN謂詞 */
SELECT dpt
  FROM Students
 GROUP BY dpt
HAVING COUNT(*) = SUM(CASE WHEN sbmt_date BETWEEN '2005-09-01' AND '2005-09-30'
                           THEN 1 ELSE 0 END);

/* 求衆數的SQL語句(1):使用謂詞all */
  SELECT income, COUNT(*) AS cnt
    FROM Graduates
   GROUP BY income
  HAVING COUNT(*) >= ALL ( SELECT COUNT(*)
                             FROM Graduates
                         GROUP BY income);
注:使用謂詞ALL,在值爲null或空集時也會出現問題可以使用極值代替

/* 求衆數的SQL語句(2):使用極值函數 */
SELECT income, COUNT(*) AS cnt
  FROM Graduates
 GROUP BY income
HAVING COUNT(*) >=  ( SELECT MAX(cnt)
                        FROM ( SELECT COUNT(*) AS cnt
                                 FROM Graduates
                             GROUP BY income) TMP) ;   

/* 求中位數的SQL語句:在HAVING子句中使用非等值自連接 */
SELECT AVG(DISTINCT income)
  FROM (SELECT T1.income
          FROM Graduates T1, Graduates T2
      GROUP BY T1.income
               /* S1的條件 */
        HAVING SUM(CASE WHEN T2.income >= T1.income THEN 1 ELSE 0 END) 
                   >= COUNT(*) / 2
               /* S2的條件 */
           AND SUM(CASE WHEN T2.income <= T1.income THEN 1 ELSE 0 END) 
                   >= COUNT(*) / 2 ) TMP; 

/*需求:查詢“提交日期”列內不包含NULL的學院*/
/* (1):使用COUNT函數 */
SELECT dpt
  FROM Students
 GROUP BY dpt
HAVING COUNT(*) = COUNT(sbmt_date);

/* (2):使用CASE表達式 */
SELECT dpt
  FROM Students
 GROUP BY dpt
HAVING COUNT(*) = SUM(CASE WHEN sbmt_date IS NOT NULL
                           THEN 1
                           ELSE 0 END);

/*需求: 查詢啤酒、紙尿褲和自行車同時在庫的店鋪 */
/* 錯誤的SQL語句 */
SELECT DISTINCT shop
  FROM ShopItems
 WHERE item IN (SELECT item FROM Items);
	注:錯誤原因in的話只要滿足其一都包含,這時候可以用HAVING來來解決問題
/* 正確的SQL語句 */
SELECT SI.shop
  FROM ShopItems SI, Items I
 WHERE SI.item = I.item
 GROUP BY SI.shop
HAVING COUNT(SI.item) = (SELECT COUNT(item) FROM Items);
	注:如果把HAVING COUNT(SI.item) = (SELECT COUNT(item) FROM Items)改成HAVING COUNT(SI.item)=COUNT(I.item),結果就會有問題,原因是受了連接操作的影響
	例:/* COUNT(I.item)的值已經不一定是3了 */
SELECT SI.shop, COUNT(SI.item), COUNT(I.item)
  FROM ShopItems SI, Items I
 WHERE SI.item = I.item
 GROUP BY SI.shop;
 
/* 精確關係除法運算:使用外連接和COUNT函數 */
  SELECT SI.shop
    FROM ShopItems AS SI LEFT OUTER JOIN Items AS I
      ON SI.item=I.item
GROUP BY SI.shop
  HAVING COUNT(SI.item) = (SELECT COUNT(item) FROM Items)   /* 條件1 */
     AND COUNT(I.item)  = (SELECT COUNT(item) FROM Items);  /* 條件2 */

/* 購物籃分析問題的一般變化  
     select 嵌套+子查詢*/
SELECT SI.shop,
       COUNT(SI.item) AS my_item_cnt,
       (SELECT COUNT(item) FROM Items) - COUNT(SI.item) AS diff_cnt
  FROM ShopItems SI, Items I
 WHERE SI.item = I.item
 GROUP BY SI.shop;

參考資料:《SQL進階教程》| MICK

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章