老規矩,先寫總結:
- 多表連接中注意事項和小結:
- 多表連接時候,當屬性列在查詢的多個表裏面是唯一的就可以省略表名前綴,否則必須加上表名前綴;
- 一張表進行自身連接時,可以給表起不同的名稱來加以區分;
- 使用外連接,可以使連接操作中的表中含有但是不滿足連接條件的也可以輸出NULL值。
- 左外連接列出左邊的關係,右外連接列出右邊關係中所有的元組(見例 [ 3.53 ] );點我跳轉
- 關係數據庫管理系統在執行多表連接時,通常是先進行兩個表的連接操作,再將其連接結果與第三個表進行連接;
- 在SQL語言中,一個select – from – where語句稱爲一個査詢塊。將一個査詢塊嵌套在另一個査詢塊的 where子句 或 having短語 的條件中的査詢稱爲嵌套査詢
- 上層的査詢塊稱爲外 層査詢或父查詢,下層査詢塊稱爲內層査詢或子査詢。
- 一個子査詢中還可以嵌套其他子査詢。但是子査詢的SELECT語句中不能使用order by子句,order by子句只能對最終 査詢結果排序。
- 子查詢的查詢條件不依賴於父查詢,稱爲不相關子查詢(見 [ 例3.55 ] )點我跳轉
- 不相關子查詢的求解方法:一般是先執行子查詢,後執行父查詢;
- 子查詢的條件依賴於父查詢,稱爲相關子查詢 (見 [ 例3.57 ])點我跳轉
- 有些嵌套查詢可以用連接運算替代,能夠用連接運算表達的査詢儘可能採用連接運算。
相關子查詢的求解方法 | 點我跳轉 |
---|---|
謂詞any 、all使用說明 | 點我跳轉 |
聚集函數和 any 、all 的對應用法 | 點我跳轉 |
Course表如下:
Student表如下:
SC表如下:
[例3.49] 查詢每個學生及其選修課程的情況。
學生信息存儲在Student表中,學生選課情況存放在SC表中,所以本査詢實際上涉及Student與SC兩個表。這兩個表之間的聯繫是通過公共屬性Sno實現的。
代碼如下:
select Student.* , SC.*
from Student , SC
where Student.Sno = SC.Sno;
結果如下圖:
[例3.50] 對【例3.49】用自然連接完成。
代碼如下:
select Student.Sno , Sname , Ssex , Sage , Sdept , Cno , Grade
from Student , SC
where Student.SNo = SC.Sno;
結果如下圖:
當屬性列在查詢的所有表裏面是唯一的就可以省略表名前綴,否則必須加上表名前綴。
本例中,由於 Sname, Ssex, Sage, Sdept,Cno 和 Grade 屬性列在 Student 表與 SC 表中是唯一的,因此引用時可以去掉表名前綴;而Sno在兩個表都出現了,因此引用時須加上表名前綴。
[例3.51] 査詢選修2號課程且成績在90分(包含90分)以上的所有學生的學號和姓名。
代碼如下:
select Student.Sno , Sname
from Student , SC
where Student.Sno = SC.Sno
and SC.Cno = '2'
and SC.Grade >= 90;
結果如下圖:
該査詢的一種優化(高效)的執行過程是,先從SC中挑選出Cno=2並且Grade>=90的元組形成一箇中間關係,再和Student中滿足連接條件的元組進行連接得到最終的結果關係。
優化查詢語句代碼如下:
select Student.Sno , Sname
from Student
where Student.Sno =
(
select SC.Sno from SC
where SC.Cno = '2'
and SC.Grade >= 90
);
結果如下:
我們可以看到,和原方法查詢結果一致。
[例3.52] 査詢每一門課的間接先修課(即先修課的先修課).
在Course表中只有每門課的直接先修課信息,而沒有先修課的先修課。要得到這個信息,必須先對一門課找到其先修課,再按此先修課的課程號査找它的先修課程。這就要將Course表與其自身連接。
代碼如下:
select first.Cno , second.Cpno
from Course first , Course second
where first.Cpno = second.Cno;
在此,我們爲Course表取了兩個別名(first 和 second);
結果如下圖:
[例 3.53] 在我們常用的連接操作中,滿足條件的元組纔可以作爲結果進行輸出,不滿足條件的就會被捨棄。如果我們想讓元組不被捨棄,並且輸出爲NULL值。我們就要用到外連接。
接下來對例【3.49】修改,輸出被捨棄的和沒被捨棄的結果:
代碼如下:
select Student.Sno , Sname , Ssex , Sage , Sdept , Cno , Grade
from Student left outer join SC
on(Student.Sno = SC.Sno);
結果如下圖:
例【3.49】輸出結果我搬到下面:
我們可以看到,被捨棄的元組又被輸出了出來。
我們要是想去除重複的值可以用修改爲以下語句:
from Student left outer join SC using(Student.Sno = SC.Sno);
左外連接列出左邊的關係(本例題),右外連接列出右邊關係中所有的元組,右連接運行結果如圖:
[例3.54] 査詢每個學生的學號、姓名、選修的課程名及成績。
此次查詢涉及了三張表,代碼如下:
select Student.Sno , Sname , Cname , Grade
from Student , SC , Course
where Student.Sno = SC.Sno
and SC.Cno = Course.Cno;
結果如下圖:
關係數據庫管理系統在執行多表連接時,通常是先進行兩個表的連接操作,再將其連接結果與第三個表進行連接。
[例3.55] 査詢與“劉晨”在同一個系學習的學生。
在這裏我們用到嵌套查詢,什麼是嵌套查詢呢?
在SQL語言中,一個select – from – where語句稱爲一個査詢塊。將一個査詢塊嵌套在另一個査詢塊的 where子句 或 having短語 的條件中的査詢稱爲嵌套査詢 ,此題目中,我們先分步來完成此査詢,然後再構造嵌套査詢:
- 先確定“劉晨”所在系名,代碼如下:
select Sdept
from Student
where Sname = '劉晨';
結果如下:
- 查找所有在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語句中不能使用order by子句,order by子句只能對最終
査詢結果排序。
顯然,嵌套查詢可以說是一步步地簡化複雜的查詢。
本例題中,子查詢的查詢條件不依賴於父查詢,稱爲不相關子查詢;
求解方法一般是先執行子查詢,後執行父查詢。
[例3.56] 查詢選修了課程名爲“信息系統”的學生學號和姓名。
本查詢涉及三個關係,我們用兩種方法實現,第一種方法代碼如下:
select Sno , Sname
from Student
where Sno in
(
select Sno
from SC
where Cno in
(
select Cno
from course
where Cname = '信息系統'
)
);
結果如下圖:
- 這裏我們同樣可以用第二種方法(連接查詢)實現,代碼如下:
select Student.Sno , Sname
from Student , SC , Course
where Student.Sno = SC.Sno
and SC.Cno = Course.Cno
and Course.Cname = '信息系統';
結果如下圖:
我們發現兩種結果相同,證明查詢正確。
有些嵌套查詢可以用連接運算替代,能夠用連接運算表達的査詢儘可能採用連接運算。
[例3.57] 找出每個學生超過(或等於)他自己選修課程平均成績的課程號。
此例題中的子查詢爲相關子查詢(子查詢的查詢條件以來於父查詢),代碼如下:
select Sno , Cno
from SC x
where Grade >=
(
select avg(Grade)
from SC y
where y.Sno = x.Sno
);
結果如下圖:
- 求解相關子查詢和求解不相關子查詢不同,內層查詢由於與外層查詢有關,所以必須反覆求值。比如這道題目的一種可能求解過程如下:
- 從外層查詢中取出SC的一個元組 x ,將元組 x 的Sno值傳遞給內層查詢。
- 執行內層查詢,可以得到一個值,用這個值替代內層查詢,代入到外層查詢中。
- 執行查詢,得到部分結果
- 判斷最外層SC元組是否處理完畢。
4.1 沒處理完畢時:外層查詢取出下一個元組,重複上述步驟。
4.2 處理完畢:輸出結果,結束。
[例3.58] 査詢非計算機科學系中比計算機科學系任意一個學生年齡小的學生姓名和年齡。
代碼如下:
select Sname , Sage
from Student
where Sage < any
(
select Sage
from Student
where Sdept = 'CS'
)
and Sdept <> 'CS';
結果如下圖:
- 這裏我們用到了 any 謂詞,怎麼用呢?
- 當子查詢返回單個值的時候我們可以用比較運算符,但是返回多個值的時候就要用到我們的 any 或者 all 謂詞。
- 使用謂詞 any 與 all 必須同時使用 比較運算符。如下表:
符號 | 效果 |
---|---|
> any | 大於子査詢結果中的某個值 |
> all | 大於子査詢結果中的所有值 |
< any | 小於子査詢結果中的某個值 |
< all | 小於子査詢結果中的所有值 |
>= any | 大於等於子査詢結果中的某個值 |
>= all | 大於等於子査詢結果中的所有值 |
<= any | 小於等於子査詢結果中的某個值 |
<= all | 小於等於子査詢結果中的所有值 |
= any | 等於子査詢結果中的某個值 |
= all | 等於子査詢結果中的所有值(通常沒有實際意義) |
!= any 或 <> any | 不等於子査詢結果中的某個值 |
!= all 或 <> all | 不等於子査詢結果中的任何一個值 |
- 本題目也可以用聚集函數來實現,代碼如下:
select Sname , Sage
from Student
where Sgae <
(
select max(Sage)
from Student
where Sdept = 'CS'
)
and Sdept <> 'CS';
這個結果我就不貼上來了。
[例3.59] 査詢非計算機科學系中比計算機科學系所有學生年齡都小的學生姓名及年齡。
這裏我們用兩種方法來寫,代碼如下:
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';
- 顯然使用聚集函數更加容易理解,並且,聚集函數實現子查詢的效率相對於使用 any 或者 all 來說高些,他們的對應表如下:
= | <> 或 != | < | <= | > | >= | |
---|---|---|---|---|---|---|
any | in | -- | < max | <= max | > min | >= min |
all | -- | not in | < min | <= min | > max | >= max |