MySQL面試試題(四)

MySQL知識點整理已經進行到第四期了,間隔一段時間再看以前的題目,驀然發現有些操作莫名其妙,可能是對錶的操作還是沒有透徹的理解。暑假可能要去實習了,做的就是SQL,希望歸來是王者~

一、分組數據

1.創建分組

之前我們整理過聚集函數,總共有5個。我們使用聚集函數可以對行進行計數,計算平均值,求和,最大值和最小值。目前爲止,所有的計算都是在表的所有數據或匹配特定的WHERE子句的數據上進行的。
比如,下面的例子返回的是供應商1003提供的產品數目:

SELECT  COUNT(*) AS  num_prods
FROM    products
WHERE   vend_id = '1003';

結果:
在這裏插入圖片描述
若要返回每個供應商提供的產品數目呢?提供10個以上的產品的供應商呢?
這裏,我們將使用分組,將數據分爲多個邏輯組,以便對每個分組進行聚集計算。
比如,我們返回每個供應商提供的產品數目,

SELECT  vend_id,COUNT(*) AS  num_prods
FROM    products
GROUP BY vend_id;

結果:
在這裏插入圖片描述
GROUP BY 按vend_id排序對每個供應商進行分組,然後對每個分組,分別計算num_prods,即提供的產品的個數。
使用GROUP BY 子句的一些規定:

  1. GROUP BY 子句可以包含任意數目的列,這使得能對分組進行嵌套,爲數據分組進行更加精細的控制。
  2. 如果在GROUP BY 子句中嵌套了分組,那麼所有的列將被一起計算,而不能從個別的列中提取數據。
  3. GROUP BY 子句列出的每個列都必須是檢索列或有效的表達式(但不能是聚集函數)。如果在SELECT中使用表達式,則必須在GROUP BY 子句中指定相同的表達式,不能使用別名。
  4. 除聚集語句外,SELECT子句的每個列都必須出現在GROUP BY 子句中出現。
  5. 如果分組列中含有NULL值,則NULL將會被作爲一個分組返回。如果列中有多行NULL值,它們將會被分爲一組。
2.過濾分組

在進行分組以後,我們需要排除一些分組,比如,列出至少有兩個以上訂單的顧客。爲了得到這種數據,必須基於完整的分組而不是個別的行進行過濾,即where子句不能使用。而HAVING子句用來過濾分組。
比如,返回訂單數不小於2的顧客,

SELECT  cust_id,COUNT(*) AS  orders 
FROM    orders
GROUP BY cust_id
HAVING   COUNT(*) >=2;

結果:
在這裏插入圖片描述
我們可以在一條語句中同時使用WHERE和HAVING子句,比如,要返回具有2個或2個以上、價格不小於10的產品的供應商。

SELECT  vend_id,COUNT(*) AS  num_prods
FROM    products
WHERE   prod_price >=10
GROUP BY  vend_id
HAVING  COUNT(*) >=2;

結果:
在這裏插入圖片描述

3.分組和排序

在這裏插入圖片描述
爲了說明GROUP BY 和ORDER BY 語句的用法,我們舉個栗子:檢索總計訂單價格不小於50的訂單的訂單號和總計訂單價格:
我們先看只有GROUP BY 的情況:

SELECT  order_num,SUM(quantity*item_price) AS ordertotal 
FROM    orderitems
GROUP BY  order_num
HAVING   SUM(quantity*item_price) >=50;

結果:
在這裏插入圖片描述
我們再看GROUP BY和HAVING同時存在的情況:

SELECT  order_num,SUM(quantity*item_price) AS ordertotal 
FROM    orderitems
GROUP BY  order_num
HAVING   SUM(quantity*item_price) >=50
ORDER BY  ordertotal;

結果:
在這裏插入圖片描述
我們可以看到order_num沒有變化,但是ordertotal按照從小到大的順序進行了排序。一般而言,ORDER BY 子句默認是升序排列,如果想逆序排列,則在後面加 DESC即可。

4.SELECT子句順序

書寫順序:
select from where group by having order by limitselect \ \to from\ \to where\ \to group\ by\ \to having\ \to order \ by\ \to limit

執行順序:
from where group by having select order by limitfrom \ \to where\ \to group\ by\ \to having\ \to select\ \to order \ by\ \to limit

二、使用子查詢

SELECT語句是SQL的查詢。我們之前用的都是從單個數據庫中檢索單條語句。但如果涉及多個表格,查詢多條語句,怎麼辦呢?SQL中使用子查詢來解決這個問題。子查詢是嵌套在其他查詢中的查詢。

1.利用子查詢進行過濾

如圖,有三個表格,第一個表格是客戶信息表 Customer,主鍵是 cust_id:顧客編號。第二個表格是訂單表 Order,主要包括 order_num—訂單號;order_date—訂單日期;cust_id—顧客編號;第二個表格通過 cust_id—顧客ID與第一個表格關聯。
第三個表格是訂單物品存儲信息 OrderItems,通過訂單號 order—num與訂單表 Order關聯。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
問題:我們想查詢訂購物品TNT2的所有顧客信息,怎麼使用子查詢?

  1. 在訂單物品存儲信息表 OrderItems 中查詢 物品TNT2 對應的 order_num—訂單號;
  2. 在訂單信息表 Orders 中查詢1中的 order_num 對應的 cust_id—顧客ID;
  3. 在顧客表 Customers 中查詢2中的 cust_id—顧客ID 對應的 cust_name 和cust_contact。

&&如果用以前的單條查詢,過程是這樣的&&

第一步:

SELECT   order_num
FROM     orderitems
WHERE    prod_id = 'TNT2';

結果:
在這裏插入圖片描述
第二步:

SELECT   cust_id
FROM     orders
WHERE    order_num IN (20005,20007);

結果:
在這裏插入圖片描述
第三步:

SELECT   cust_name,cust_contact
FROM     customers
WHERE    cust_id   IN  (10001,10004);

結果:
在這裏插入圖片描述
&&使用子查詢進行嵌套&&

SELECT   cust_name,cust_contact
FROM     customers
WHERE    cust_id   IN  (SELECT   cust_id
                        FROM     orders
                        WHERE    order_num IN (SELECT   order_num
                                               FROM     orderitems
                                               WHERE    prod_id = 'TNT2'));

結果:
在這裏插入圖片描述
當然,解決這個問題不止這一種方法,

SELECT   cust_name,cust_contact
FROM     customers a,orders b,orderitems c
WHERE    c.prod_id = 'TNT2' AND  c.order_num = b.order_num  AND  b.cust_id  =  a.cust_id;

結果也是一樣的。

2.作爲計算字段使用子查詢

使用子查詢的另一種方法是創建計算字段。比如,要計算某個特定客戶的訂單數,我們可以這樣查詢:

SELECT   COUNT(*) AS  orders 
FROM     orders
WHERE    cust_id = '10001';

結果:
在這裏插入圖片描述
但如果要查詢每個顧客的訂單數呢?

SELECT   c.cust_id,
         cust_name,
         (SELECT COUNT(*)
          FROM   orders o
          WHERE  o.cust_id = c.cust_id) AS orders 
FROM     customers c
ORDER BY orders desc;

select中的 orders 是一個計算字段,它是由圓括號中的子查詢建立的。該子查詢對檢索出的每個客戶執行一次。
結果:
在這裏插入圖片描述
創建子查詢的技巧:
在語句的複雜性不斷增加的情況下,子查詢的創建很有技巧性。用子查詢建立(和測試)查詢的最可靠的方法是逐行進行,這與MySQL處理它們的方法非常相同。

  1. 建立和測試最內層的查詢
  2. 用硬編碼數據建立和測試外層的查詢,並且僅在確認它正常後才嵌入子查詢
  3. 再次測試,對於要增加的每個查詢,重複這些步驟。

四、經典面試題

問題描述:
本題中用到下面三個關係:
CARD 借書卡:CNO 卡號,NAME 姓名,CLASS 班級;
BOOKS 書籍:BNO 書號,BNAME 書名,AUTHOR 作者,PRICE 價格,QUANTITY數量;
BORROW 借書記錄:CNO 借書卡號,BNO 書號,RDATE 還書日期;
備註:規定每人每種書只能借一本;庫存冊數隨借書、還書而改變。

-- 創建借書卡表格
##############################################################
CREATE  TABLE  CARD(
    CNO    INT  NOT NULL PRIMARY KEY,
    NAME   VARCHAR(50),
    CLASS  VARCHAR(20)
)ENGINE = INNODB;
-- 創建圖書表格
CREATE   TABLE  BOOKS(
    BNO    INT   NOT NULL PRIMARY KEY,
    BNAME  VARCHAR(50),
    AUTHOR VARCHAR(255),
    PRICE  DECIMAL(4,2),
    QUANTITY INT
)ENGINE = INNODB;
-- 創建借書記錄表格
CREATE    TABLE  BORROW(
    CNO    INT,
    BNO    INT,
    RDATE  DATETIME,
    FOREIGN KEY (CNO) REFERENCES CARD(CNO),
    FOREIGN KEY (BNO) REFERENCES BOOKS(BNO)
)ENGINE = INNODB;
#################################################################
-- 填充數據
############################################################################################################
INSERT  INTO  CARD(CNO,NAME,CLASS)  VALUES(2019001,'張長武','c01');
INSERT  INTO  CARD(CNO,NAME,CLASS)  VALUES(2019002,'李雲龍','c01');
INSERT  INTO  CARD(CNO,NAME,CLASS)  VALUES(2019003,'特朗普','c03');
INSERT  INTO  CARD(CNO,NAME,CLASS)  VALUES(2019004,'霍金','力01');
INSERT  INTO  CARD(CNO,NAME,CLASS)  VALUES(2019005,'錢三強','力01');
INSERT  INTO  CARD(CNO,NAME,CLASS)  VALUES(2019006,'楊振寧','力03');
INSERT  INTO  CARD(CNO,NAME,CLASS)  VALUES(2019007,'華羅庚','m01');
INSERT  INTO  CARD(CNO,NAME,CLASS)  VALUES(2019008,'陳景潤','m02');
INSERT  INTO  CARD(CNO,NAME,CLASS)  VALUES(2019009,'菲爾普斯','m03');
#############################################################################################################
INSERT  INTO  BOOKS(BNO,BNAME,AUTHOR,PRICE,QUANTITY)  VALUES(1001,'水滸','施耐庵',45.20,6);
INSERT  INTO  BOOKS(BNO,BNAME,AUTHOR,PRICE,QUANTITY)  VALUES(1002,'計算機網絡基礎','張仲利',67.80,7);
INSERT  INTO  BOOKS(BNO,BNAME,AUTHOR,PRICE,QUANTITY)  VALUES(1003,'網絡安全概述','李麗',89.90,12);
INSERT  INTO  BOOKS(BNO,BNAME,AUTHOR,PRICE,QUANTITY)  VALUES(1004,'計算方法','王毅',23.20,4);
INSERT  INTO  BOOKS(BNO,BNAME,AUTHOR,PRICE,QUANTITY)  VALUES(1005,'計算方法習題冊','張磊',21.00,8);
INSERT  INTO  BOOKS(BNO,BNAME,AUTHOR,PRICE,QUANTITY)  VALUES(1006,'組合數學','Peter Harrington',69.00,3);
INSERT  INTO  BOOKS(BNO,BNAME,AUTHOR,PRICE,QUANTITY)  VALUES(1007,'數據庫技術技術及應用','鬼手',39.00,1);
INSERT  INTO  BOOKS(BNO,BNAME,AUTHOR,PRICE,QUANTITY)  VALUES(1008,'統計學習方法','李航',30.00,3);
##############################################################################################################
INSERT  INTO  BORROW(CNO,BNO,RDATE)  VALUES(2019005,1005,'2019-06-20');
INSERT  INTO  BORROW(CNO,BNO,RDATE)  VALUES(2019005,1006,'2019-06-29');
INSERT  INTO  BORROW(CNO,BNO,RDATE)  VALUES(2019005,1003,'2019-02-01');
INSERT  INTO  BORROW(CNO,BNO,RDATE)  VALUES(2019005,1002,'2019-07-02');
INSERT  INTO  BORROW(CNO,BNO,RDATE)  VALUES(2019005,1007,'2019-08-02');
INSERT  INTO  BORROW(CNO,BNO,RDATE)  VALUES(2019005,1004,'2019-06-30');
INSERT  INTO  BORROW(CNO,BNO,RDATE)  VALUES(2019009,1004,'2019-06-26');
INSERT  INTO  BORROW(CNO,BNO,RDATE)  VALUES(2019009,1006,'2019-07-23');
INSERT  INTO  BORROW(CNO,BNO,RDATE)  VALUES(2019006,1004,'2019-03-12');
INSERT  INTO  BORROW(CNO,BNO,RDATE)  VALUES(2019001,1001,'2019-10-27');
INSERT  INTO  BORROW(CNO,BNO,RDATE)  VALUES(2019001,1004,'2019-09-10');
INSERT  INTO  BORROW(CNO,BNO,RDATE)  VALUES(2019002,1001,'2019-08-12');
INSERT  INTO  BORROW(CNO,BNO,RDATE)  VALUES(2019002,1006,'2019-05-20');
INSERT  INTO  BORROW(CNO,BNO,RDATE)  VALUES(2019003,1001,'2019-06-20');
###############################################################################################################
SELECT  * FROM  CARD;
SELECT  * FROM  BOOKS;
SELECT  * FROM  BORROW;
###############################################################################################################
-- 一、找出借書超過5本的讀者,輸出借書卡號、借書人姓名以及所借圖書冊數
SELECT   C.CNO,NAME,COUNT(BNO)
FROM     BORROW B INNER JOIN CARD C
ON       B.CNO = C.CNO
GROUP BY CNO
HAVING   COUNT(BNO) > 5;
-- 二、查詢借閱了“水滸”一書的讀者,輸出姓名及班級
SELECT  NAME,CLASS
FROM    CARD
WHERE   CNO  IN (SELECT CNO 
                 FROM   BORROW
                 WHERE  BNO IN  (SELECT  BNO
                                 FROM    BOOKS
                                 WHERE   BNAME = '水滸'));
-- 三、查詢過期未還的圖書,輸出借閱者(卡號)、書號及還書日期
-- current_date()返回當前的日期和時間
SELECT  CNO,BNO,RDATE
FROM    BORROW
WHERE   RDATE < current_date();
-- 四、查詢書名包括“網絡”關鍵詞的圖書,輸出書號、書名、作者
SELECT  BNO,BNAME,AUTHOR
FROM    BOOKS
WHERE   BNAME LIKE '%網絡%';
-- 五、查詢現有圖書中價格最高的圖書、輸出書名及作者
SELECT   BNO,AUTHOR,PRICE
FROM     BOOKS
ORDER BY PRICE DESC LIMIT 0,1;
-- 六、查詢當前借了“計算方法”但沒有借“計算方法習題集”的讀者,輸出其借書卡號,並按卡號降序排序輸出
SELECT   a.CNO
FROM     BORROW a ,BOOKS b
WHERE    b.BNAME = '計算方法' AND  a.BNO = b.BNO
         AND   NOT   EXISTS(
               SELECT  *
               FROM    BORROW  a1 , BOOKS  b1
               WHERE   a1.BNO  =  b1.BNO   AND   b1.BNAME = '計算方法習題集'  AND   a1.CNO = a.CNO);

-- 七、將“c01”班的同學所借圖書的還期延長一週
SELECT   b.CNO,BNO,DATE_ADD(RDATE,INTERVAL 7 DAY)
FROM     BORROW b INNER JOIN  CARD c
WHERE    b.CNO = c.CNO  AND  c.CLASS = 'c01';
-- 八、從BOOKS表中刪除當前無人借閱的圖書記錄
DELETE   FROM   BOOKS  WHERE  BNO = (SELECT    c.BNO
                                     FROM     (SELECT  BNO  FROM  BOOKS  WHERE  BNO  NOT  IN (SELECT BNO
                                                                                                 FROM   BORROW)) AS c);
SELECT * FROM  BOOKS;
-- 九、如果經常按書名查閱圖書信息,請建立合適的索引
####################報錯###########################
CREATE CLUSTERED INDEX IDX_BOOKS_BNAME ON BOOKS(BNAME)
####################報錯###########################
-- 十、在BORROW表上建立一個觸發器,完成如下功能:如果讀者借閱的書名是"數據庫技術及應用",就將該讀者的借閱記錄保存在BORROW_SAVE表中(注ORROW_SAVE表結構同BORROW
####################-報錯-###########################
CREATE   TRIGGER   TR_SAVE  ON   BORROW
FOR  INSERT,UPDATE
AS
IF   @ @ROWCOUNT > 0
INSERT  BORROW_SAVE  SELECT i.*
FROM    INSERTED i,BOOKS b
WHERE   i,BNO = b.BNO
AND   b.BNAME = '數據庫技術及應用';
####################-報錯-###########################
-- 十一、建立一個視圖,顯示“力01”班學生的借書信息(只要求顯示姓名和書名)
INSERT  INTO  BORROW(CNO,BNO,RDATE)  VALUES(2019004,1001,'2019-07-20');
DELETE   FROM  BORROW WHERE  CNO = 2019004;
#######################################################################
SELECT  NAME,BNAME
FROM    CARD c ,BORROW b,BOOKS o
WHERE   c.CNO = b.CNO AND CLASS = '力01' AND  b.BNO = o.BNO;
#######################################################################
-- 十二、查詢當前同時借有“計算方法”和“組合數學”兩本書的讀者,輸出其借書卡號,並按卡號升序排序輸
SELECT a.CNO
FROM   (SELECT CNO FROM BORROW WHERE BNO IN (SELECT BNO FROM  BOOKS WHERE BNAME = '計算方法')) AS a,
       (SELECT CNO FROM BORROW WHERE BNO IN (SELECT  BNO  FROM BOOKS  WHERE  BNAME = '組合數學'))AS b
WHERE   a.CNO = b.CNO;
-- 十三、假定在建BOOKS表格時沒有定義主碼,寫出爲BOOKS表追加定義主碼的語句
ALTER   TABLE  BOOKS  ADD  PRIMARY  KEY(BNO);
-- 十四、將NAME最大列寬增加到60個字符(假定原爲50個字符)
ALTER  TABLE  CARD  MODIFY NAME VARCHAR(60);
-- 十五、爲該表增加1列NAME(系名),可變長度,最大字符20個
ALTER  TABLE  CARD  ADD  系名 VARCHAR(20);
SELECT  * FROM   CARD;

參考資料:
1.《MySQL必知必會》 Ben Forta;
2.sql面試題(學生表_課程表_成績表_教師表):https://www.cnblogs.com/qixuejia/p/3637735.html
3.常用數學符號的 LaTeX 表示方法:http://mohu.org/info/symbols/symbols.htm

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