本文主講數據查詢中的 連接查詢 和 嵌套查詢(部分),歡迎閱讀~
一、連接查詢
連接查詢: 同時涉及兩個及以上的表的查詢
- 連接條件: 用來連接兩個表的條件
[<表名1>.]<列名1> <比較運算符> [<表名2>.]<列名2>
比如:Student.Sno = SC.Sno
- 連接字段: 連接條件中的列名稱
如:Sno就是上面例子中的連接字段
(ps:連接字段類型必須是可比的,但名字不必相同)
1. 等值與非等值連接查詢
當連接運算符爲=
時,稱爲等值連接;使用其他運算符稱爲非等值連接。
等值連接: 連接運算符爲 =
🌟來看例子: 查詢每個學生及其選修課程的情況:
SELECT Student.*, SC.*
FROM Student, SC
WHERE Student.Sno = SC.Sno;
- 關係代數中的等值連接:StudentSC
(這裏爲了省事兒,SELECT Student.*, SC.*
可簡寫爲SELECT *
)
如圖等值連接即直接把兩個表拼在一起了,會有重複的列,如圖中的Sno,一會兒要寫的自然連接就是去掉了重複列。
- 這裏如果不寫條件即沒有WHERE條件表達式,則打印出的是兩個表的笛卡爾積(如下圖,列數即兩表的列數之和,行數即兩表的行數之積,所以有10*9=90個元組,5+3=8個屬性列)
自然連接
🌟來看例子: 用自然連接完成上面的那個例子:(即去掉重複列Sno)
SELECT Student.Sno, Sname, Ssex, Sage, Sdept, Cno, Grade
FROM Student, SC
WHERE Student.Sno = SC.Sno;
一條SQL語句可以同時完成選擇和連接查詢
🌟來看例子: 查詢選修2號課程且成績在80分以上的所有學生的學號和姓名:
SELECT Student.Sno, Sname
FROM Student, SC
WHERE Student.Sno = SC.Sno AND
SC.Cno = '2' AND SC.Grade > 80;
SELECT *
FROM Student, SC
WHERE Student.Sno = SC.Sno; /*把總的打印出來對照*/
· 執行過程:
先從SC中選擇出Cno='2’且Grade>90的元組形成一個中間關係,再和Student中滿足連接條件的元組進行連接得到最終的結果(這裏講解操作爲主,具體的執行過程、方法等我將會在【吐血整理】系列裏詳細介紹)
(ps:連接操作的執行方法有嵌套循環法、排序合併法等等,不同的DBMS執行方法可能會有所區別,不過出發點都是因爲連接查詢比較費時間,通過採用先進的高效的方法來優化,提高效率)
2. 自身連接
自身連接: 一個表與其自己進行連接
· 需要給表起別名以示區別(這裏只是給它起別名來當作兩個或多個表,當實際上它仍然只是一個表,我們仍然只是在對同一個表進行操作)
· 所有屬性名都是同名屬性,因此必須使用“別名”
🌟來看例子: 查詢每一門課的間接先修課(即先修課的先修課):
SELECT FIRST.Cno, SECOND.Cpno
FROM Course FIRST, Course SECOND
WHERE FIRST.Cpno = SECOND.Cno;
該題求間接先修課,拿下圖中的 1 數據庫 課程舉例:它的先修課Cpno是 5 數據結構,所以數據結構的課程號Cno是等於數據庫的Cpno的,而數據結構的先修課Cpno是 7 Pascal語言,所以 1 數據庫 的間接先修課就是 7 Pascal語言
3. 外連接
外連接與普通連接的區別:
- ⋈ 普通連接操作只輸出滿足連接條件的元組
- 外連接操作以指定表爲連接主體,將主體表中不滿足連接條件的元組一併輸出
外連接分三類:左外連接(LEFT OUTER JOIN
)、右外連接(RIGHT OUTER JOIN
)、全外連接(FULL OUTER JOIN
)
(ps:OUTER可省略)
⟕ 左外連接: 列出左邊關係中所有的元組 (即還返回左表中不符合連接條件只符合查詢條件的數據行)
⟖ 右外連接: 列出右邊關係中所有的元組 (即還返回右表中不符合連接條件只符合查詢條件的數據行)
⟗ 全外連接:不僅返回左表中不符合連接條件只符合查詢條件的數據行,並且還返回右表中不符合連接條件只符合查詢條件的數據行(全外連接實際是上左外連接和右外連接的數學合集(去掉重複),即全外連接 = 左外連接 UNION 右外連接
)
🌟來看例子: 改寫上面等值連接的例子:查詢每個學生及其選修課程的情況:
1)左外連接
SELECT Student.Sno, Sname, Ssex, Sage, Sdept, Cno, Grade
FROM Student LEFT OUTER JOIN SC ON (Student.Sno = SC.Sno); /*LEFT OUTER JOIN可簡寫爲LEFT JOIN*/
SELECT *
FROM Student, SC
WHERE Student.Sno = SC.Sno; /*對照等值連接*/
對比等值連接,這裏的左外連接把Student學生表中Cno課程號爲空和Sdept爲空(不符合連接條件只符合查詢條件的數據行)的也都打印了出來,即沒選課的學生也在其查詢範圍內。
(ps:左外連接的查詢結果更適用於班主任(瞭解全班學生的選課情況),而科任老師更喜歡等值連接的結果,因爲課程空的學生就是沒選他課的學生,不屬於自己操心的範圍)
2)右外連接
SELECT Student.Sno, Sname, Ssex, Sage, Sdept, Cno, Grade
FROM Student RIGHT OUTER JOIN SC ON (Student.Sno = SC.Sno); /*RIGHT OUTER JOIN可簡寫爲RIGHT JOIN*/
SELECT *
FROM Student, SC
WHERE Student.Sno = SC.Sno; /*對照等值連接*/
可見SC表中沒有不符合連接條件只符合查詢條件的數據行,即SC表的所有元組都符合連接條件
3)全外連接
SELECT Student.Sno, Sname, Ssex, Sage, Sdept, Cno, Grade
FROM Student FULL OUTER JOIN SC ON (Student.Sno = SC.Sno); /*FULL OUTER JOIN可簡寫爲FULL JOIN*/
SELECT *
FROM Student, SC
WHERE Student.Sno = SC.Sno; /*對照等值連接*/
因爲右表(即SC表)沒有不符合連接條件只符合查詢條件的數據行,所以這裏的全外連接結果等於左外連接。
4. 多表連接
多表連接即 兩個以上的表進行連接
🌟直接來看例子: 查詢每個學生的學號、姓名、選修的課程名及成績:
SELECT Student.Sno, Sname, Cname, Grade
FROM Student, SC, Course /*多表連接*/
WHERE Student.Sno = SC.Sno
AND SC.Cno = Course.Cno;
SELECT *
FROM Student, SC, Course
WHERE Student.Sno = SC.Sno
AND SC.Cno = Course.Cno; /*老規矩,打印粗來對照*/
該題姓名只有Student表有,選修的課程名只有Course表有,成績只有SC表有,所以需要用到三張表
二、嵌套查詢
嵌套查詢:
· 一個SELECT-FROM-WHERE
語句稱爲一個查詢塊
· 將一個查詢塊嵌套在另一個查詢塊的WHERE子句或HAVING短語的條件中的查詢稱爲嵌套查詢
例如 如下形式:
SELECT Sname /*外層查詢/父查詢*/
FROM Student
WHERE Sno IN
( SELECT Sno /*內層查詢/子查詢*/
FROM SC
WHERE Cno= '2');
SQL語言允許多層嵌套查詢: 即一個子查詢中還可以嵌套其他子查詢
子查詢的限制: 不能使用ORDER BY子句
- 不相關子查詢: 子查詢的查詢條件不依賴於父查詢
由裏向外 逐層處理: 即每個子查詢在上一級查詢處理之前求解,子查詢的結果用於建立其父查詢的查找條件
例如:
SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept IN
( SELECT Sdept
FROM Student
WHERE Sname = '劉晨');
- 相關子查詢: 子查詢的查詢條件依賴於父查詢
首先取外層查詢中表的第一個元組,根據它與內層查詢相關的屬性值處理內層查詢,若WHERE子句返回值爲真,則取此元組放入結果表
然後再取外層表的下一個元組
重複這一過程,直至外層表全部檢查完爲止
(如下代碼塊所示:從外層查詢中取出SC的一個元組x,將元組x的Sno值傳送給內層查詢,執行內層查詢,得到值AVG(Grade)
,用該值代替內層查詢,得到外層查詢)
SELECT Sno, Cno
FROM SC x
WHERE Grade >= ( SELECT AVG (Grade)
FROM SC y
WHERE y.Sno = x.Sno);
1. 帶有IN謂詞的子查詢
🌟直接來看例子: 查詢與“劉晨 ”在同一個系學習的學生:
(這裏的題意爲:知道劉晨,但不知道她在哪個系,所以找與她在同一個系的學生需要先知道她是那一個系的)
方法一:分步完成
① 確定“劉晨”所在系名
SELECT Sdept
FROM Student
WHERE Sname = '劉晨';
查詢結果爲:CS,用作第二步的查詢條件
② 查找所有在CS系學習的學生。
SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept = 'CS';
方法二:將第一步查詢嵌入到第二步查詢的條件中
SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept IN
( SELECT Sdept
FROM Student
WHERE Sname = '劉晨');
此查詢爲 不相關子查詢
方法三:用自身連接(關鍵在於起別名!)
SELECT S1.Sno, S1.Sname, S1.Sdept
FROM Student S1, Student S2
WHERE S1.Sdept = S2.Sdept AND S2.Sname = '劉晨';
這個連接查詢的意思可理解爲: 列出Sdept系別相同的所有學生,且劉晨是這個系別的
(ps:對於這個題來說,嵌套查詢比連接查詢在邏輯上要更清晰一些)
🌟Another one: 查詢選修了課程名爲“信息系統”的學生學號和姓名:
利用帶有IN謂詞的子查詢:
SELECT Sno, Sname /* 3.最後在Student關係中取出Sno和Sname */
FROM Student
WHERE Sno IN
( SELECT Sno /* 2.然後在SC關係中找出選修了3號課程的學生學號 */
FROM SC
WHERE Cno IN
( SELECT Cno /* 1.首先在Course關係中找出“信息系統”的課程號,爲3號 */
FROM Course
WHERE Cname = '信息系統'
)
);
用連接查詢實現:
SELECT Student.Sno, Sname /*注意!!採用連接查詢時這裏Sno一定要寫清楚表頭是哪個表!!!*/
FROM Student, SC, Course
WHERE Student.Sno = SC.Sno AND
SC.Cno = Course.Cno AND
Course.Cname = '信息系統';
· 3層及以上的查詢採用連接查詢會更簡潔一些,但是理解起來帶IN謂詞的嵌套查詢要容易理解一些,看情況而用吧。
2. 帶有比較運算符的子查詢
當能確切知道內層查詢返回單值時,可用比較運算符(>,<,=,>=,<=,!= 或 <>均表示不等於)
🌟來看例子: 同上面的 查詢與“劉晨”在同一個系的學生:
(由於一個學生只可能在一個系學習,則可以用 =
代替IN )
SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept =
( SELECT Sdept
FROM Student
WHERE Sname = '劉晨');
🌟Another one: 找出每個學生超過他選修課程平均成績的課程號:
(即上面講相關子查詢時候的例子)
SELECT Sno, Cno
FROM SC x
WHERE Grade >= ( SELECT AVG (Grade)
FROM SC y
WHERE y.Sno = x.Sno);
SELECT * FROM SC;
SELECT * FROM SC; /*打印出整張表對照*/
SELECT AVG(Grade)
FROM SC
WHERE Sno = '201215121'; /*這裏我就只打印其中一個學生的平均分叭,要想看其他的換學號就可*/
(執行過程:從外層查詢中取出SC的一個元組x,將元組x的Sno值傳送給內層查詢,執行內層查詢,得到值AVG(Grade)
,用該值代替內層查詢,得到外層查詢)
3. 帶有ANY(SOME)或ALL謂詞的子查詢
使用ANY或ALL謂詞時必須同時使用比較運算
語義爲:
> ANY
:大於子查詢結果中的某個值(> ANY 表示至少大於一個值,即大於最小值)
> ALL
:大於子查詢結果中的所有值(> ALL 表示大於每一個值,即大於最大值)< ANY
:小於子查詢結果中的某個值(< ANY 表示至少小於一個值,即小於最大值)
< ALL
:小於子查詢結果中的所有值(< ALL 表示小於每一個值,即小於最小值)>= ANY
:大於等於子查詢結果中的某個值(大於等於最小值)
>= ALL
:大於等於子查詢結果中的所有值(大於等於最大值)
如果還是不太清楚的話,趕緊來看看例子叭~
🌟來看例子: 查詢 非 計算機科學系中比計算機科學系 任意一個 學生年齡小的學生姓名和年齡:
SELECT Sname, Sage
FROM Student
WHERE Sage < ANY (SELECT Sage
FROM Student
WHERE Sdept = 'CS')
AND Sdept <> 'CS'; /*這是父查詢塊中的另一個條件:非CS系*/
SELECT Sname, Sage
FROM Student
WHERE Sage <
( SELECT MAX (Sage)
FROM Student
WHERE Sdept = 'CS ') /*這裏題中要求爲比任意一個學生小,所以就是小於其最大值*/
AND Sdept <> 'CS';
SELECT * FROM Student
採用ANY謂詞嵌套查詢的執行過程:
1)首先處理子查詢,找出CS系中所有學生的年齡,構成一個集合(20,19)
2)處理父查詢,找所有不是CS系且年齡小於20或19(即小於最大值20)的學生
(ps:如果ANY對於你還是不太好理解的話,把它當成SOME應該能好理解一點,SOME 與 ANY 是等效的,即可以在SQL語句中把ANY換成SOME,效果一樣。可參考官方文檔:用 ANY、SOME 或 ALL 修改的比較運算符)
🌟Another one: 查詢 非 計算機科學系中比計算機科學系 所有 學生年齡都 小 的學生姓名及年齡:
方法一:用ALL謂詞
SELECT Sname, Sage
FROM Student
WHERE Sage < ALL (SELECT Sage
FROM Student
WHERE Sdept = 'CS')
AND Sdept <> 'CS';
方法二:用聚集函數(小於所有的,即小於其最小值)
SELECT Sname, Sage
FROM Student
WHERE Sage <
( SELECT MIN(Sage)
FROM Student
WHERE Sdept = 'CS')
AND Sdept <> 'CS';
採用ALL謂詞嵌套查詢的執行過程:
1)首先處理子查詢,找出CS系中所有學生的年齡,構成一個集合(20,19)
2)處理父查詢,找所有不是CS系且年齡小於20和19(即小於最小值19)的學生
🔥小總結:ANY(或SOME),ALL謂詞與聚集函數、IN謂詞的等價轉換關係 :
那 本文到這裏也就結束啦,感謝你耐心地閱讀~😊,嵌套查詢中的重難點:帶有EXISTS謂詞的子查詢將會在下一篇中介紹 ~
這裏是一個想把學習過程記錄成博客分享給大家的undergraduate,請多關照🙏
咱們下期 見~