1. 程式人生 > >子查詢與連線使用指南

子查詢與連線使用指南

 子查詢就是查詢中又巢狀的查詢,巢狀的級數隨各資料庫廠商的設定而有所不同,一般最大巢狀數不超過15級,實際應用中,一般不要超過2級,否則程式碼難以理解.一般來說,所有巢狀子查詢都可改寫為非巢狀的查詢,但是這樣將導致程式碼量增大.子查詢就如遞迴函式一樣,有時侯使用起來能達到事半功倍之效,只是其執行效率同樣較低,有時用自身連線可代替某些子查詢,另外,某些相關子查詢也可改寫成非相關子查詢.

   子查詢常用於複雜的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 )

        )