Oracle 11g——SQL 巢狀查詢
前言
在前面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 大於子查詢結果中的某個值
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的效率更好。對應關係表如下:
四。帶有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謂詞的子查詢替換。