1. 程式人生 > >Oracle 11g——SQL 巢狀查詢

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謂詞的子查詢替換。