徐愛蕓
摘 要:SQL對數據執行最多的操作是對數據的查詢,查詢的方法不唯一,因此查詢的速度和效率差別也很大。本文分析了幾種查詢的方法:相關子查詢、無關子查詢、集合的查詢,分析查詢執行的過程,并對每一種查詢方法進行了定量的分析,從而得出了比較優化的查詢方法。
關鍵詞:SQL語句;相關子查詢;無關子查詢;集合查詢;查詢優化
1前言
子查詢也稱嵌套查詢,它嵌套在 SELECT、INSERT、UPDATE、DELETE 語句或其它子查詢中,任何允許使用表達式的地方都可以使用子查詢。如果子查詢的條件中使用了其外層的表的字段,這種子查詢就叫作相關子查詢。相關子查詢可以用EXISTS、NOT EXISTS引入。子查詢可以計算一個變化的聚合函數值,并返回到外圍查詢進行比較。子查詢比較靈活、方便,常作為增刪改查的篩選條件,適合于操縱一個表的數據。但是在性能上,往往一個不合適的子查詢用法會形成一個性能瓶頸。
2子查詢分類
一個查詢語句塊(select-from-where)可以嵌套在另外一個查詢塊的where子句中,從子查詢的語法規則和執行過程不同,子查詢分為無關子查詢和相關子查詢兩種。
2.1無關子查詢
無關子查詢的結構如下:SELECT ——FROM——WHERE? (SELECT ——FROM——WHERE)
無關子查詢的工作方式是由內向外處理:先執行內查詢,外層查詢利用內層查詢的結果作條件,子查詢返回結果的數據類型必須與外查詢 WHERE 語句的數據類型相匹配。
2.2相關子查詢
相關子查詢的結構如下:SELECT ——FROM——WHERE? NOT? EXISTS (SELECT? *? ?FROM——? WHERE? NOT? EXISTS? ? (SELECT? * FROM —— WHERE? ?));
帶有EXISTS量詞的子查詢不返回任何數據,只產生邏輯值,子查詢的查詢條件依賴于外層父查詢的某個屬性值,這就是相關子查詢的由來,內查詢的執行次數與外查詢執行次數相同。
3查詢執行過程分析
以下面的三張表為例子,要求查詢被所有學生都選修了的課程信息。
Student(Sno ,Sname,Sdep ) Course(Cno, Cname, Credit)? SC(Sno, Cno, Score)
3.1采用相關子查詢
根據傳統的方法,我們采用三層嵌套,對應的SQL語句如下:
SELECT Cno, Cname? ?FROM? Course? ?WHERE NOT EXISTS (SELECT? *? FROM Student WHERE NOT EXISTS? ? ?(SELECT * FROM SC WHERE Sno=Student.Sno AND Cno=Course.Cno));
這個算是一個比較復雜的sql語句了,兩個 NOT EXISTS和三個WHERE,這個sql語句可以分為3層:最外層語句,最內層語句,中間層語句。
其執行過程如下:①進入第一層:在外層Course中取出一個元組的Cno,如Cno值‘C1;②進入第二層:取Student表中的一個Sno值‘S1;③進入第三層:判斷SC表中是否有 Cno=‘C1 并且Sno=‘S1這樣的元組,經過NOT EXISTS的取反,存在F,不存在就是T—— 結果F④返回第二層:取第二個Sno值‘S2⑤進入第三層:判斷是否存在 Cno=‘C1 并且Sno=‘S2這樣的元組,結果取反 —— 結果F;⑥經過多次這樣的迭代,取最后一個Sno值,結果取反 —— 結果F,返回第一層的結果:T(NOT EXISTS再次取反),所以將Cno='C1課程信息取出放入結果表中,即對應Course的'C1記錄,Student表中的所有的記錄對應的中間層的返回值為假,所以最外層的NOT EXISTS對應的值為真,最外層的WHERE的值也為真,則'C1對應的課程的記錄符合查詢條件,裝入結果表中。
重復上述步驟,直到將Course中的元組取完。
這是一個帶有全稱量詞的謂詞查詢(題中帶有“全部”),將其轉換為等價的存在量詞的謂詞查詢,即:不存在一個學生沒有選修這門課程的,即所有學生都選了這門課程。
可以看出:相關子查詢從上往下順序執行,主查詢的每一行查詢都執行一次完整的子查詢。EXISTS 操作符檢查在子查詢中是否存在滿足條件的行:如果在子查詢中存在滿足條件的行:不在子查詢中繼續查找,條件返回 TRUE;如果在子查詢中不存在滿足條件的行,條件返回 FALSE, 繼續在子查詢中查找。假設學生表有10條記錄,課程表有8條記錄,選課表按平均每人選6門課算,則整個查詢要執行10×8×6=480次。
3.2 采用綜合查詢法
如果將外層設計為相關子查詢,而在中間層為無關子查詢,對應的SQL語句如下:SELECT Cno,Cname? FROM Course WHERE NOT EXISTS (SELECT * FROM Student WHERE Sno? NOT IN (SELECT Sno? FROM? SC? WHERE Cno=C.Cno)) ;
其執行過程如下:① 進入第一層:在外層Course中取出一個元組的Cno,如Cno值‘C1;② 進入第二層:執行無關子查詢,按照無關子查詢執行的順序,先執行最內層的查詢,在選課表中查詢選修了C1課程的學生的學號,返回到中間層;③ 經過NOT? IN 的取反,得到的是沒有選修‘C1課程的學生的學號;④ 根據相關子查詢的執行原理,將中間層的結果返回到第一層,再一次經過NOT EXISTS的取反,得到的是不存在沒有選修‘C1課程的學生,說明所有學生都選修了‘C1課程。
可以看出:這里采用了相關子查詢和無關子查詢兩種方法,同樣按上面的例子,由于第二層和第三層之間采用的無關子查詢,只需要在子查詢中查找滿足條件的元組,不需要外層查詢中的字段值,整個查詢要執行10×8=80次。顯然,這種方法更好理解,且只有一層相關子查詢,執行速度快。
3.3 采用集合的查詢
SQL的SELECT語句查詢結果是元組的集合,如果將外層設計為相關子查詢,而把內層設計為集合的查詢,集合操作可以用差運算。這樣三層嵌套查詢可以用兩層嵌套查詢完成。對應的SQL語句如下:SELECT? Cno,Cname? FROM C? WHERE NOT EXISTS? (( SELECT? Sno? ?FROM S)? ? EXCEPT? ?(SELECT Sno? FROM? SC? )? WHERE Cno=C.Cno? ) ;
其執行過程如下:① 進入第一層:在外層Course中取出一個元組的Cno,如Cno值‘C1;② 進入第二層:執行集合的子查詢,在學生表中投影全部學生學號Sno,同時在選課表中查選修了‘C1課程的學生學號,然后執行集合的差運算,得到的是沒有選修‘C1課程的學生學號,返回到外層;③ 經過NOT? EXISTS的取反,得到的是不存在沒有選修‘C1課程的學生,說明所有學生都選修了‘C1課程。
采用了相關子查詢和集合查詢,同樣按上面的例子,只有兩層查詢,則整個查詢要執行10次。顯然,這種方法最簡單,方法直接,易理解,執行速度最快,效率最高。
結束語
一個查詢是可以用多種方法來實現的,一個復雜的查詢要用到嵌套查詢,相關子查詢最不好理解,執行的過程長,效率低;如果采用無關子查詢、集合查詢等,可以加快查詢的速度,提高查詢效率。
參考文獻
[1] 姜代紅,蔣秀蓮 .數據庫原理及應用(第2版)清華大學出版社,2010.12
[2] 何玉潔 數據庫系統教程(第2版)人民郵電出版社,2015.12