MySQL面試試題(五)

壬戌之秋,七月既望。眼看就要七月了,回首六月,好像離預期的目標還是太遠。大規模仿真實驗還在跑跑跑,參數還在調調調,糟心之餘,唯一欣慰的是,總共完成了十篇博客,涵蓋MySQL和機器學習。本次主要是總結MySQL中的聯結組合查詢

一、聯結表

在闡述聯結這個詞之前我們先了解一些概念:
關係表
舉個例子,這裏有一個包含產品目錄的數據庫,

prod_id—產品編號
vend_id—供應商編號
prod_name—產品名稱
prod_price—產品價格
prod_desc—產品描述
在這裏插入圖片描述
如果同一供應商生產多種物品,那麼如何儲存供應商消息?
一般的,我們會重新創建一個表格,來儲存供應商的信息。因爲可能會有多個產品對應同一供應商,如果放在同一表格的話,會造成數據重複。如果放在不同的表格,一旦供應商信息發生改變,只需要修改一次即可。
因此,我們爲供應商單獨建立了一個表格,
在這裏插入圖片描述
在關係型數據庫中,各個表通過某一字段關聯。Products表只儲存產品信息,包括了供應商ID,vend_id表的主鍵又叫products的外鍵,它將products表和vendors關聯起來。
外鍵:外鍵爲某一個表的一個字段,同時又爲另一個表的主鍵。
爲什麼要使用聯結?
前面我們講了,關係型數據庫中多個表通過某一字段關聯,這樣更方便處理,更有效率。但問題是如果數據儲存在多個表中,怎麼樣用單條SELECT語句把我們想要的數據檢索出來呢?使用聯結可以解決這個問題。聯結是一種機制,用來在一條SELECT語句中關聯表。

1.創建聯結

創建一個聯結,返回供應商名稱以及提供的產品,

SELECT  vend_name,prod_name,prod_price
FROM    vendors,products
WHERE   vendors.vend_id = products.vend_id;

結果:
在這裏插入圖片描述
在聯結兩個表時,第一個表的每一行與第二個表的每一行配對。WHERE子句作爲過濾條件,它只包含那些匹配給定條件的行。如果沒有WHERE子句,第一個表格的每一行會與第二個表格的每一行配對。
在這裏插入圖片描述

2.內部聯結

我們之前在WHERE子句中所使用的聯結用等號連接,稱爲等值聯結,它基於兩個表格的相等測試。這種聯結也叫內部聯結

SELECT  vend_name,prod_name,prod_price
FROM    vendors INNER JOIN products
ON   vendors.vend_id = products.vend_id;

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

3.聯結多個表

一條SELECT語句可以聯結多個表,數目沒有限制,創建表的規則相同。

SELECT  prod_name,vend_name,prod_name,quantity
FROM    products a,vendors b,orderitems c
WHERE   a.vend_id = b.vend_id AND a.prod_id = c.prod_id AND order_num = 20005;

結果:
在這裏插入圖片描述
在第十四章中,我們要列出訂購物品TNT2的所有客戶,之前使用的是子查詢,由內到外依次展開,

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'));

這裏使用聯結,其中WHERE子句中包含三個條件,前兩個關聯聯結中的表,後一個是過濾產品ID爲TNT2的數據。

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

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

二、創建高級聯結

除了內聯結以外,還有其他的聯結類型。

1.使用表別名

爲了縮短SQL語句,允許在單條SELECT語句中多次使用相同的表,我們使用別名。別名除了用於列名和計算字段之外,還可以用於表。其實前面我們已經使用過。

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

我們給三個表格分別取名a,b,c,在where子句裏,我們就可以使用這些別名,而不用寫全稱。

2.使用不同類型的聯結

前面我們在select語句中使用過內部聯結(等值聯結),這裏再介紹三種聯結方式
1)自聯結
問題背景:假如你發現某物品(其ID爲DTNTR)存在問題,因此想知道生產該物品的供應商生產的其他物品是否也有問題。
第一種方式:使用子查詢

SELECT   prod_id,prod_name
    FROM     products
    WHERE    vend_id  IN(SELECT vend_id 
                         FROM   products
                         WHERE  prod_id = 'DTNTR');

結果:
在這裏插入圖片描述
第二種方式:使用自聯結

SELECT   b.prod_id ,b.prod_name
FROM     products a,products b
WHERE    a.vend_id = b.vend_id AND a.prod_id = 'DTNTR';

結果相同,不再展示。使用的兩個表是同一個表,實際上是同一個表使用了兩次。

2)自然聯結
自然聯結其實和內部聯結一樣,只是不會出現相同的重複列。

SELECT  c.*,o.order_num,o.order_date,oi.prod_id,oi.quantity,Oi.item_price
FROM    customers AS  c,orders AS o,orderitems AS oi
WHERE   c.cust_id = o.cust_id AND oi.order_num = o.order_num AND  prod_id = 'FB';

結果:
在這裏插入圖片描述
字體太小,但我們可以發現,沒有重複的列出現。
而如果你直接使用內部聯結,

SELECT  *
FROM    vendors INNER JOIN products
ON   vendors.vend_id = products.vend_id;

結果:
在這裏插入圖片描述
可以發現,內部聯結把將兩個表中存在聯結關係的字段符合聯結關係的那些記錄形成記錄集的聯結。這裏就是把 id 相等的部分羅列出來。
3)外部聯結
外部聯結包含了在聯結表中沒有關聯行的行,比如

  • 對每個客戶下了多少訂單進行計數,包括哪些沒有下訂單的客戶;
  • 列出所有產品以及訂購數量,包括沒有被訂購的產品;

外部聯結包括左聯結(left outer join on)和右聯結(right outer join on)。
我們把內部聯結和外部聯結對比一下這裏是查詢顧客對應的訂單:
內部聯結:
SELECT a.cust_id,order_num
FROM customers a INNER JOIN orders b
ON a.cust_id = b.cust_id;
結果:
在這裏插入圖片描述
外部聯結:

SELECT  a.cust_id,order_num
FROM    customers a LEFT OUTER JOIN orders b
ON      a.cust_id = b.cust_id;

結果:
在這裏插入圖片描述
從這兩個結果,我們可以發現,內部聯結表把兩個表中符合id相等的行取出來,而外部聯結中,左邊的行一定會列出,右邊的表中如果沒有匹配的行,則列值爲Null。特別需要注意的是如果右表有多行和左表匹配,那麼左表相同的行會出現多次。

3.使用帶聚集函數的聯結

之前的聚集函數只是在一個表中進行彙集數據。如果在多個表中彙集數據,需要使用聯結。
比如,要檢索所有客戶以及每個客戶下的訂單數,

SELECT  cust_name,c.cust_id,COUNT(*) AS order_num
FROM    orders o INNER JOIN customers c
ON   o.cust_id = c.cust_id
GROUP BY  o.cust_id;

結果:
在這裏插入圖片描述
如果要包含那些沒有下訂單的顧客,

SELECT  cust_name,c.cust_id,COUNT(order_num) AS order_num
FROM    orders o RIGHT OUTER JOIN  customers c
ON   o.cust_id = c.cust_id
GROUP BY  c.cust_id;

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

  • 不要忘記提供聯結條件,否則會得出笛卡爾積;
  • 一個聯結中可以包含多個表,每個聯結可以包含不同的聯結類型。在一起測試這些聯結之前,應該分別測試每個聯結,利於排除故障。

三、組合查詢

之前我們所做的大部分查詢都是包含從一個或多個表中返回查詢結果的單條SELECT語句。MySQL允許多個查詢(多條SELECT語句),並將結果作爲單個查詢結果返回。這些組合查詢也被稱爲並-union或複合查詢。
適用情況:

  • 在單個查詢中從不同的表返回類似結構的數據;
  • 對單個表執行多個查詢,按單個查詢返回數據。
1.創建組合查詢

UNION操作符來組合多條SELECT查詢,並將結果組合成單個結果集。
在使用UNION時,只需要在每條select語句之間加上關鍵字UNION即可。
這裏,我們要將兩個SELECT語句進行組合,
第一個是檢索價格不高於5的物品,

SELECT   vend_id,prod_id,prod_price
FROM     products
WHERE    prod_price <=5;

結果:
在這裏插入圖片描述
第二個是找出供應商1001和1002生產的所有物品,

SELECT   vend_id,prod_id,prod_price
FROM     products
WHERE    vend_id  IN (1001,1002);

結果:
在這裏插入圖片描述
我們將這兩條語句進行組合,

SELECT   vend_id,prod_id,prod_price
FROM     products
WHERE    prod_price <=5
UNION
SELECT   vend_id,prod_id,prod_price
FROM     products
WHERE    vend_id  IN (1001,1002);

結果:
在這裏插入圖片描述
當然這種比較簡單的查詢,我們可以只用 WHERE + OR 語句

SELECT   vend_id,prod_id,prod_price
FROM     products
WHERE    prod_price <=5 OR vend_id  IN (1001,1002);
2.使用UNION

有三條基本規則:

  • UNION必須由兩條或兩條以上的SELECT語句組成,語句之間用關鍵字UNION分隔
  • UNION中的每個查詢必須包含相同的列、表達式或聚集函數(不過各個列不需要以相同的次序列出)
  • 列的數據類型必須兼容:類型不必完全相同

去重

UNION從查詢結果集中自動去除了重複的行,如果想返回所有匹配的行,可使用UNION ALL

SELECT   vend_id,prod_id,prod_price
FROM     products
WHERE    prod_price <=5
UNION ALL
SELECT   vend_id,prod_id,prod_price
FROM     products
WHERE    vend_id  IN (1001,1002);

結果:
在這裏插入圖片描述
這裏就包括了重複的行。一般地,UNION 幾乎完成與多個 where 子句相同的工作。UNION ALLUNION 的一種形式,它完成WHERE子句完成不了的工作。如果確實需要每個條件的匹配行全部出現,則必須使用 UNION ALL而不是 WHERE

組合查詢結果排序

單條select語句中的排序用 ORDER BY 排序。在用 UNION組合查詢時,只能使用一條 ORDER BY ,它必須出現在最後一條 SELECT 語句之後。對於結果集,不允許用一種方式排序一部分,而又用另一種方式排序另一部分,不允許使用多條ORDER BY 子句。

對之前的組合結果使用ORDER BY 子句

SELECT   vend_id,prod_id,prod_price
FROM     products
WHERE    prod_price <=5
UNION ALL
SELECT   vend_id,prod_id,prod_price
FROM     products
WHERE    vend_id  IN (1001,1002)
ORDER BY vend_id,prod_id;

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

四、經典面試題

問題描述如下:
爲管理崗位業務培訓信息,建立三個表格:

  • Stu(Sid,SN,SD,SA)表:學生表
    Sid 表示學號
    SN 表示學員姓名
    SD 表示所屬單位
    SA 表示學員年齡

  • Cou(Cid,CN)表:課程表
    Cid 課程編號
    CN 課程名稱

  • Sco(Sid,Cid,G)表:分數表
    Sid 表示學號
    Cid 課程編號
    G 學習成績

具體問題參考SQL腳本

################################################################################################################

-- 創建學生表

CREATE  TABLE  Stu(
   Sid  INT NOT NULL  PRIMARY  KEY,   
   SN   VARCHAR(20),
   SD   VARCHAR(50),
   SA   INT
 )ENGINE = INNODB;
-- 創建課程表
CREATE  TABLE  Cou(
   Cid   VARCHAR(10)  NOT  NULL  PRIMARY  KEY,
   CN    VARCHAR(20)
)ENGINE = INNODB;
-- 創建成績表
CREATE   TABLE Sco(
   Sid   INT  NOT NULL,
   Cid   VARCHAR(10)  NOT NULL,
   G     DECIMAL(5,2),
   CONSTRAINT  fk_Sid  FOREIGN KEY  (Sid)  REFERENCES  Stu(Sid),
   CONSTRAINT  fk_Cid  FOREIGN KEY  (Cid)  REFERENCES  Cou(Cid)
)ENGINE = INNODB;
#################################################################################################################

-- 填充數據

INSERT  INTO  Stu(Sid,SN,SD,SA)   VALUES(2019001,'托馬斯李','運營',26);
INSERT  INTO  Stu(Sid,SN,SD,SA)   VALUES(2019002,'米高揚','管理',30);
INSERT  INTO  Stu(Sid,SN,SD,SA)   VALUES(2019003,'蝙蝠俠','安防',22);
INSERT  INTO  Stu(Sid,SN,SD,SA)   VALUES(2019004,'李嘉誠','投資',45);
INSERT  INTO  Stu(Sid,SN,SD,SA)   VALUES(2019005,'雷軍','開發',34);
INSERT  INTO  Stu(Sid,SN,SD,SA)   VALUES(2019006,'周小川','管理',56);
INSERT  INTO  Stu(Sid,SN,SD,SA)   VALUES(2019007,'陸奇','運營',36);
INSERT  INTO  Stu(Sid,SN,SD,SA)   VALUES(2019008,'普京','安防',67);

INSERT  INTO  Cou(Cid,CN)   VALUES('C1','稅收基礎');
INSERT  INTO  Cou(Cid,CN)   VALUES('C2','金融工程');
INSERT  INTO  Cou(Cid,CN)   VALUES('C3','會計');
INSERT  INTO  Cou(Cid,CN)   VALUES('C4','統計學習方法');
INSERT  INTO  Cou(Cid,CN)   VALUES('C5','大數據');
INSERT  INTO  Cou(Cid,CN)   VALUES('C6','機器學習算法');

INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019001,'C2',80);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019002,'C2',78);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019003,'C1',89);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019003,'C5',60);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019004,'C4',90);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019005,'C1',87);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019005,'C2',75);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019005,'C3',80);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019005,'C4',90);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019005,'C5',86);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019005,'C6',88);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019006,'C1',99);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019006,'C2',61);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019007,'C1',62);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019007,'C2',78);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019007,'C3',77);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019007,'C4',69);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019007,'C5',98);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019007,'C6',88);
INSERT  INTO  Sco(Sid,Cid,G) VALUES(2019008,'C1',78);
#################################################################################################################

-- 一、使用標準SQL嵌套語言查詢選修課程名稱爲“稅收基礎”的學員學號與姓名

SELECT    Sid,SN
FROM      Stu 
WHERE     Sid   IN  (SELECT  Sid  
                     FROM    Sco
                     WHERE   Cid  IN  (SELECT  Cid  
                                       FROM    Cou 
                                       WHERE   CN  =  '稅收基礎'));
-- 二、使用標準SQL嵌套語言查詢選修課程編號爲“C2”的學員姓名和所屬單位

SELECT   SN,SD
FROM     Stu 
WHERE    Sid   IN  (SELECT  Sid
                    FROM    Sco
                    WHERE   Cid = 'C2');
-- 三、使用標準SQL嵌套語言查詢選修課程編號爲“C5”的學員姓名和所屬單位

SELECT  SN,SD
FROM    Stu p1 INNER JOIN  Sco  p2
ON      p1.Sid  =  p2.Sid  AND  p2.Cid  =  'C5';
-- 四、使用標準SQ嵌套語言查詢選修全部課程的學員姓名和所屬單位

SELECT  SN,SD
FROM    Stu a ,Sco b
WHERE   a.Sid = b.Sid
GROUP BY  b.Sid
HAVING  COUNT(Cid) =  (SELECT COUNT(Cid)
                       FROM   Cou); 
-- 五、查詢選修課程的學員人數

SELECT   CN,COUNT(Sid)
FROM     Cou s1 INNER JOIN Sco s2
ON       s1.Cid  =  s2.Cid
GROUP BY s2.Cid;
-- 六、查詢選修課程超過5門的學員學號和所屬單位

SELECT   s2.Sid,SD
FROM     Sco s1 INNER JOIN  Stu  s2
ON       s1.Sid  =  s2.Sid  
GROUP BY  s1.Sid
HAVING    COUNT(Cid) > 5;

參考資料:
1.《MySQL必知必會》 Ben Forta;
2.sql面試題(學生表_課程表_成績表_教師表):https://www.cnblogs.com/qixuejia/p/3637735.html

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