摘要:在SQL SERVER環境下對查詢符號、聯合查詢、多條件,聯接運算等幾個常見的SQL優化問題進行了分析研究,指出了由于優化器的參與,純理論的優化建議對SQL用戶的實踐可能產生的誤導。
關鍵詞:SQL;優化;SQL SERVER;數據庫
中圖分類號:TP311文獻標識碼:A 文章編號:1009-3044(2008)15-21002-02
The Method of Optimization in SQL Server
WU Xian-feng
(Sichuan Institute of Administration,Chengdu 610004,China)
Abstract:This paper research some questions of SQL performance optimization with SQL SERVER such as operator, UNION operation, Multi-search condition, JION operation. Considers that, because of Models of optimizing in database, some suggestion of optimization base on pure theory would misadvise user of SQL.
Key words: SQL; optimization; SQL SERVER; database
1 引言
SQL查詢的優化是數據庫應用領域的一個重要問題,關于這個問題的討論很多。但遺憾的是很多討論更多的從SQL語句的語法和構造的層面來進行說明,脫離具體的數據庫環境。事實上,優化問題是一個實踐性很強的問題,數據庫在執行語句的時候,并非完全按SQL語句中規定的邏輯和層次來執行,數據庫會對SQL語句進行解析后,按特定的方式來執行。不同的數據庫處理同樣的SQL語句也會有區別。
這里我們結合SQL SERVER對比較常見的幾種優化建議進行探討。
2 查詢符號的優化
在條件表達式中,查詢符號的使用可能影響到索引能否被使用,從而影響查詢的效率,一般遵循這樣一些原則:不要使用表達式,盡量避免負邏輯,避免語句出現LIKE“%”等,此外,研究者一般認為,使用or連接條件時,會被強制使用全表掃描,也有人認為exist的效率遠遠低于in 的效率。
我們選用一個有10萬行的表在SQL SERVER環境下進行測試,通過SQL SERVER提供的查詢計劃圖形顯示,我們很容易得到查詢是按何種方式展開的。結果證明在where子句中,如果在字段上使用了代數表達式或函數時,使用LIKE“%”,查詢方式是全表掃描,但是在負邏輯,or連接條件等情況下,卻并不一定采用全表掃描的方式。
仔細分析其過程,可以看出,SQL SERVER優化器首先預測結果行的數量,如果結果行數量較多時,則無論是負邏輯還是正邏輯,均采用全表掃描。如果結果行較少,我們可稱其為具有強選擇性,則優化器會自動將負邏輯轉化為等價的正邏輯表達,然后在索引上掃描。
同樣,在分析or連接時,也可以看到,執行計劃同樣有可能在索引上掃描,是全表掃描還是掃描索引,不是取決于使用and或是or連接,而是取決于對結果行數量的預測。即使是在or的兩端是不同的字段時,如:discount=1 or ordered=10000 ,只要存在相關的索引,查詢時均可使用這些索引。
與此相關的另一個問題是,許多人認為or符號連接條件將被強制全表掃描,所以建議用UNION來替代OR符號,以提高效率,事實上這也是得不償失的。因為,一般情況下,在UNION中可以用到的索引在or符號表達時同樣可以用到。在少數情況下,確實存在使用or符號時不能利用索引,而在用UNION表達時可以利用索引的情況。但由于UNION操作中多了合并插入、去除重復行等工作,所以總體效率還是會低于在單一的SELECT語句中用OR符號。
3 聯合查詢時的優化
在聯合(union)運算時,一般認為需要考慮的問題是,在SQL語句中各個SELECT語句的順序問題。主張將結果行相對較少的行放在靠后,這樣可以減少插入操作。
但在SQL SERVER中,我們針對這一問題設計含兩個SELECT子句的UNION語句,使其兩個SELECT語句中返回的行數又較大的差別,可以看到這樣的結果:
(1)如果兩個SELECT語句中,如果小結果語句具有強選擇性,因而利用了索引掃描。這種情況下,大結果放在前面效率較高。
(2)如果兩個SELECT語句中均不具有強選擇性,但該表中有聚集索引時,SQL SERVER對兩部分結果的合并采用Merge Union運算。
我們以一個具有1000個返回行的結果集為小結果,并將與其組合的大結果集的行數做多種變化,得到以下一組數據(見表1):

數據表明,在這種情況下,大結果語句放在靠考前時,效率反而較低。
(3)如果兩個SELECT均不具有強選擇性,并且而該表上沒有聚集索引時,SQL SERVER對兩部分結果的合并采用Hash Union運算。測試表明,這種情況下,效率并不會受到SELECT語句次序的影響。
4 多條件的優化
這里討論的多條件優化指的是在where 子句中有多個條件并用and連接時,條件的次序對SQL語句執行的效率影響,一般認為,將嚴格的條件放在前面,弱條件放在后面具有較高的效率。原因是,強條件可以得到較小的臨時表,在此基礎之上再進行弱條件篩選總體效益較高。
試驗表明,SQL SERVER在處理多條件時,條件的執行順序并不完全按其在where子句中的順序來執行,而是與該條件覆蓋的索引有著密切的關系(見表2):

這里的索引是非聚集索引,如果其中的條件覆蓋聚集索引時情況稍有不同。由此我們可以看出,多條件時,條件的執行順序會被優化器作恰當的安排,由于存在索引的因素,系統的優化也不是簡單的將強條件先行執行。而開發人員在構造SQL語句的時候,則沒有必要去考慮where子句中條件的順序問題。
5 聯接運算的優化
聯接運算優化中的一個問題是:當一個SQL中既有聯接運算,同時也存在的選擇運算時,應當采用先選擇運算縮小聯接運算的范圍,避免聯接運算產生較大的臨時表。所以有人舉出以下典型的例子:
方法一:SELECT * FROM products ,address WHERE products.productid = address. Productid and date = {^2005 - 1 - 1}
方法二:SELECT * FROM address WHERE productid in (SELECT productid FROM products WHERE date= {^2005 - 1 - 1})
事實上這兩種方法的結果集并非完全等價,因為在in操作符在處理嵌套語句的結果集時,會自動剔出重復的值。因此,它的結果等同于address和products之間為一對多的關系時的聯接結果。換而言之,如果address和products之間是多對多的關系時,兩種方法的結果是不一致的,第一種方法的結果集將大于第二種方法。所以,嚴格而言,無論第二種方法的性能如何,都不能算是一種優化方法。
如果address和products為一對多的關系時兩種方法結構相同,那是否存在效率上的差異?由SQL SERVER的查詢計劃可以看到:優化器在處理這兩種方式時,采用的是同一種策略,總是先作選擇運算,然后在作聯接運算。無論兩表的聯接是以join的方式表達,還是在where子句中表達,抑或是以嵌套查詢的方式表達,其實現方式和代價是完全一樣的。
6 結語
以上對SQL語句優化的一部分問題作了實踐性的探討,可見,由于數據庫系統在執行SQL指令時均有一個優化過程,因此,在討論SQL語句的優化問題時,應當結合具體環境,結合優化器的處理法則。僅僅從語法上作邏輯上的解釋和想象所提出的優化對SQL用戶的實踐可能是一種誤導。
參考文獻:
[1] 王書海,劉明生,馬銀華.基于多表連接的分組查詢語句的性能分析與優化[J].計算機工程,2000,26(7):186-187.
[2] 谷震離. 查詢語句對SQL Server 數據庫查詢性能優化分析 [J].福建電腦,2007,3:21-22.
[3] 王振輝,吳廣茂.SQL查詢語句優化研究[J].計算機應用,2005,25(12):207-208.
[4] 楊庚.關系數據庫SQL語言查詢過程的分析和優化設計[J],計算機工程與應用,1999,11:87-88.
[5] [美]微軟公司著.Querying Microsoft SQL Server 2000 With Transact-SQL[M].北京:清華大學出版社,2001.
[6] [美]微軟公司著.Programming a Microsoft SQL Server 2000 Database[M].北京:清華大學出版社,2001.
[7] 苗雪蘭,劉瑞新,王懷峰. 數據庫系統原理及應用教程(第2版)[M]. 北京: 機械工業出版社,2004.