子查詢與連線使用指南
子查詢常用於複雜的SQL操作中,包括select,insert,delete,update等語句中都可巢狀子查詢.子查詢分為兩種:相關子查詢和不相關子查詢,顧名思義,相關子查詢不能獨自執行,必須依賴於外部父查詢提供某些值才能執行.
子查詢可以返回:結果集,邏輯值.僅當使用exists子句時,子查詢才不返回結果集,而只返回true或false這樣的邏輯值.可用於子查詢的關鍵字有:IN,ANY(任一),ALL等.具體的說,子查詢可返回單值,元組(即兩個或兩個以上值),及多值結果集.
下面舉一些例子來說明子查詢的使用:
1. 假設資料表emp中有empID和empName兩個欄位,其中empID(主鍵)有重複,現在要求刪除表中empID值有重複的記錄,並且必須保留一條empID值重複的記錄.
解決思路:用相關子查詢和自身連線即可,下面是實現的SQL語句
Delete from emp e1 where empID in ( select empID from emp where empID = e1.empID and empName != e1.empName )
2. 用子查詢來更新資料庫表.假設資料表salary中有2個欄位empID和salaryAmount,資料表emp中有2個欄位empID和Age,請用emp表中的Age欄位來代替salary表中的salaryAmount欄位的值,下面是實現的SQL語句
Update salary S Set salaryAmount = ( select Age from emp where empID = S.empID )
說明:在(相關)子查詢中也可使用未出現在父查詢中的欄位值,如上例中的empID欄位
3. 連線:連線分為4種,分別是內連線(inter join),外連線(outer join,分為左,右和全外連線三種),交叉連線(cross join)和自身連線(self join).外連線的關鍵字和識別符號有:left,right,full,(+),*等.當為(+)標識時,則靠近(+)的表為副表,而遠離(+)的表則為主表(這依賴於資料庫實現,在Oracle中是這樣規定的 )
4. 假設有客戶表customer,其中欄位有custID,custName,Addr,客戶定貨表orders,其中欄位有 orderID,custID,orderDate,procID.請查詢出在2000年1月1日以後有訂貨的客戶及(沒有訂貨的)所有客戶
Select custID,custName,Addr,orderDate from customer left join orders on
(customer.custID = orders.custID) and ( orderDate >'2000-1-1')
說明:左外連線,則左邊的表為主表,在本例中,表customer即為主表;右外連線,則右邊的表即為主表. 若不用坐外連線,則2000年1月1日前未訂貨的客戶則查詢不出來.一般來說,如果要使查詢中不遺漏任何記錄,則只有用全外連線(full outer join)才行.
5. 查詢的6個關鍵字有:select,from,where,group by,having,order by,以上是它們在查詢語句中一般應出現的順序.having與group by的關係就猶如where 與select的關係一樣,都是用於限定哪些行被選中.以下是一些注意事項:
- 子查詢中不能排序,即不能有order by子句
- order by子句中的欄位可以不出現在select子句後的欄位列表中,但但當有distinct限定詞時,order by中的欄位必須出現在select子句中.
- 子查詢可以巢狀,並且是從裡往外開始執行
6.假設有學生表student,其中有欄位sno,sname,birthday,課程表course,其中有欄位cno,cname,ccent,學生選課表sc,其中有欄位cno,sno,grade
- 請查詢出未選修任何課的學生
select sno,sname from student S where not exists
( select sno from sc where cno in
( select cno from course where cno = sc.cno and sc.sno = s.sno )
)
- 查詢出未被任何學生選修的課程號及課程名
select cno,cname from course where cno not in ( select cno from sc )
- 刪除未被任何學生選修的課程
delete from course where cno in ( select cno from course where cno not in
( select cno from sc )
)
7.並union,交intersect,差minus運算
- 請查詢出同時選修了2門以上課程的學生名單,如英語和漢語
select * from student where sno in
( select sno from sc
where cno in
( select cno from course where cname ='英語' )
)
union
select * from student where sno in
( select sno from sc
where cno in
( select cno from course where cname ='漢語' )
)
- 查詢出選修了英語而未選修漢語的學生名單
select * from student where sno in
( select sno from sc
where cno in
( select cno from course where cname ='英語' )
)
minus
select * from student where sno in
( select sno from sc
where cno in
( select cno from course where cname ='漢語' )
)
- 查詢出既選修了英語又選修了漢語的學生名單
select * from student where sno in
( select sno from sc
where cno in
( select cno from course where cname ='英語' )
)
intersect
select * from student where sno in
( select sno from sc
where cno in
( select cno from course where cname ='漢語' )
)
8.刪除或修改子句不能加join條件,如:delete from table1 where condition1 and condition2 類似這樣的語句不合法,即condition1條件後不能有condition2或更多的條件,解決的辦法可以用子查詢,如:
delete from table1 where ( ... ),同樣,對update 子句也類似
9.內連線是等值連線,所以當兩個表間不匹配時,很容易遺漏資料,解決的辦法是用外連線,但無論是左外連線還是右外連線,在理論上都只能使遺漏的資料少些,只有全外連線才能保證不遺漏資料.
需要注意NULL(空值)對(子)查詢的影響,比如下例,如要查詢出A和B表中存在相同ID的等級時,就必須限定ID不為NULL,否則子查詢返回空結果集:
select A.ID,A.Level,A.desc from table1 A
where A.Level in
( select B.Level from table2 B where
( A.ID = B.ID and B.ID is not null )
)