前言
在前面2個章節,我們比較詳細的介紹了一些SQL語句的基本用法,但是在我們實際的項目開發中,其實很多時候這些基本的用法遠遠不能滿足我們項目的需求,這個時候就需要我們的嵌套查詢。
在SQL語句中,一個select-from-where語句稱為一耳光查詢快。將一個查詢快嵌套在另外一個的where子句或having 短語的條件的查詢稱為嵌套查詢(Nested Query)。
比如,先舉一個簡單的例子:
select Sname --------------外查詢語句塊------- from Student where Sno in --------------外查詢語句塊------- ( --------------內查詢語句塊------- select Sno From Sc where Cno=2
--------------內查詢語句塊------- );
查詢語句塊分為外查詢語句或叫父查詢以及內查詢或子查詢。
NOTE:SQL語言允許多層嵌套查詢,即一個子查詢中還可以嵌套其他子查詢。需要特別指出的是,子查詢的select語句中不能使用order by字句,order by 只能對最終查詢結果排序。
一。帶有in謂詞的子查詢
在嵌套查詢中,子查詢往往是一個集合,所以謂詞in是嵌套查詢中最經常使用的謂詞。
【例1】查詢與“XiaoHong”在同一個系的學生
OK,我們一步一步的來分解這個查詢步驟:
①確定“XiaoHong”所在的系
select sdept from student where sname='XiaoHong'
得到的結果是:CS。
②查找所有在CS系學習的所有學生
select sno,sname,sdept from student where sdept='CS';
ok,得到如下結果:
③合並操作。
現在我們只需要把上面的操作合並在一起就好了!
select sno,sname,sdept from student where sdept in ( select sdept from student where sname='XiaoHong' );
得到的結果也是一樣。
本例中,因為子查詢的查詢條件不依賴於父查詢,所以稱為不相關查詢。
當然本例中也還可以有其他解法:自連接方法如下:
select s1.sno,s1.sname,s1.sdept from student s1,student s2 where s1.sdept=s2.sdept and s2.sname='XiaoHong';
可見,一個查詢有很多種方法,當然不同的方法執行的效率可能會有很大的不同。
【例2】查詢選修了課程名為”Math“的學生學號和姓名
看似這一句很短,其實這里有2個子查詢。我們需要先弄清楚里面的關系,然后在來進行解答
思路如下:
1).課程名在course表中才有,所以我們從course表中選出課程名為Math的課程號(Cno)
2).選出課程號之后,在在SC表中,找到課程號所對應的學生號
3).在student表中找出對應的學號和學生名字。
select sno,sname --第三步,最后從student表中取出數據出來。 from student where sno in ( select sno --第二步。。找出SC關系表中的選修了2號課程的學生學號 from sc where cno in ( select cno -- 第一步。。找出course表中的課程名為math的課程號。結果為2. from course where cname='Math' ) );
結果如下:
代替方法:
select student.sno,sname from student,sc,course where course.cname='Math' and course.cno=sc.cno and sc.sno=student.sno;
結果也是一樣滴。
二。帶有比較運算符的子查詢
帶有比較運算符的子查詢是指父查詢之間用比較運算符進行連接。當用戶能確切知道內存查詢返回的是單值時,可以用>,<,>=,<=,!=,<>等等比較運算符。
【例3】和例1 是一樣的:查詢與“XiaoHong”在同一個系的學生
因為一個學生只可能在一個系學習,也就是說內查詢的結果是一個值,因此可以用“=”代替“in”
如下:
select sno,sname,sdept from student where sdept = ( select sdept from student where sname='XiaoHong' );
這樣 也能得到和例1的結果。
需要注意的是,子查詢一定要跟在比較符之后,下面寫法是錯誤的:
----------------錯誤寫法---------------------
select sno,sname,sdept from student where ( select sdept from student where sname='XiaoHong') =sdept;
----------------錯誤寫法---------------------
【例4】找出每個學生超過他選修課課程平均成績的課程號
這個怎么理解呢?我們先把結果寫出來,然后再來剖析它。
select sno,cno from sc x where grade>= ( select avg(grade) from sc y where y. sno=x.sno);
x是Sc表的別名,y也是一樣。內存查詢是求一個學生所有選修課程平均成績,至於是哪個學生的平均成績要看s.sno的值。,而該值是和父查詢有關的,所以這類查詢叫做相關子查詢。
執行過程可能如下:
①從外層查詢中取出SC表中的一行(也就是元組 x),將x的值sno
select avg(Grade) from sc y where y.sno=1;
得到結果是98.5
②執行內查詢得到的結果是:98.5,所以得到外查詢是:
select sno,cno from sc x where Grade>=98.5;
得到結果如下:
然后,外層查詢取出下一個元組重復做上面的動作。。。
得到的結果是:
三。帶有any(some)或all謂詞的子查詢
子查詢返回單值可以用比較算術法,但是返回多值時要用any(有些系統用some)或者all謂詞修飾符。而是用any或all謂詞時則必須同時是用比較運算符。
>any 大於子查詢結果中的某個值
<any 小於子查詢結果中的某個值
>all 大於子查詢結果中的所有值
<all 小於子查詢結果中的所有值
>=any大於等於子查詢結果中的某個值
<=any小於等於子查詢結果中的某個值
>=all 大於等於子查詢結果中的所有值
<=all 小於等於子查詢結果中的所有值
=any 等於子查詢結果中的中的某個值
=all 等於子查詢結果中的所有值(通常沒有實際意思)
!=(或<>)any 不等於子查詢結果中的某個值
!=(或<>)all 不等於子查詢結果中的的任何一個值
【例5】查詢其他系中比計算機科學系某一個學生年齡小的學生姓名和年齡
SQL語句如下:
select sname,sage from student where sage < any ( select sage from student where sdept='CS' ) and sdept <> 'CS'; -- 注意這是父查詢的條件
數據庫執行執行此查詢的時候,首先處理子查詢,找出CS系中所有學生的年齡,找出一個集合(18,22)。任何處理父查詢,找出所有不是CS系並且年齡小於18或22的學生即可。
本查詢也可以用聚集函數來實現。首先用子查詢查出CS系中最大年齡(22),任何在父查詢中找出所有非CS系並且年齡小於22歲的學生即可。
select sname,sage from student where sage < ( select max(sage) from student where sdept='CS' ) and sdept<>'CS'
也可以得到上面的結構。結果如下:
【例6】查詢其他系中比計算機科學系所有學生年齡都小的學生姓名和年齡
經過上面的分析,這個很簡單,如下
select sname,sage from student where sage <all ( select sage from student where sdept='CS' ) and sdept<> 'CS';
這里需要向大家說聲對不起,剛開始構建數據的時候,沒有構建好,所以這里查詢為空,為了試驗的完整性,我這里修改一下數據結構。
update student set sage=17 where sname='XiaoZhang';
ok,我們現在在執行剛才的操作,可以得到如下結果。
本查詢也可以使用聚集查詢來實現,如下:
select sname,sage from student where sage < ( select min(sage) from student where sdept='CS' ) and Sdept !='CS';
事實上,聚集函數實現子查詢通常比直接用all或any的效率更好。對應關系表如下:
= | <>或!= | < | <= | > | >= | |
ANY | IN | == | <Max | <=Max | >Min | >=Min |
ALL | == | NOT IN | <Min | <=Min | <Max | <=Max |
四。帶有exists謂詞的子查詢
帶有exists謂詞的子查詢不返回任何數據,只產生邏輯真或假,也就是true或false。
【例6】查詢所有選修了1號課程的學生姓名
select sname from student where exists ( select * from sc where Sno=Student.Sno and Cno=1 )
那上面的執行流程是怎么樣的呢?
①從外查詢student表中取出一行數據
②把這條數據的Sno和內查詢Sc表中的Sno進行比較
③若比較結果為真,則把這數據的sname放入一個結果集合中。
④然后再取student表的嚇一條數據,直到取完為止。
使用exists量詞后,若內存結果非空,則外層的where字句返回真值,否則返回假值。
由於exists引出的子查詢,起目標列表達式通常都是“*”,因為帶有exists的子查詢只返回true或false,給出列名無具體意思。
exists謂詞相對應的是not exists。使用not exists后,如果內存查詢結果為空,則外層的where字句返回真值,否則返回假值。
【例7】查詢沒有選修了1號課程的學生姓名
select sname from student where not exists ( select * from sc where Sno=Student.Sno and Cno=1 )
一些帶exists或not exists謂詞的子查詢不能被其他形式的子查詢等價替換,但是其他帶有in謂詞、比較運算符、any、all謂詞的子查詢都能用exists謂詞的子查詢替換。
啊