爲何SQL語句Where 1=1 and在SQL Server中不影響性能

    最近一個朋友和我探討關於Where 1=1 and這種形式的語句會不會影響性能。最後結論是不影響。算法

    雖然結論正確,但對問題的認識卻遠遠沒有解決問題的根本。實際上在T-SQL語句的書寫過程當中常常犯得錯誤就是得出一個很窄的結論,而後教條式的奉若聖經,對於T-SQL領域來講,在網上常常能夠看到所謂的優化守則,隨便在網上搜了一些摘錄以下:數據庫

  • 不要有超過5個以上的錶鏈接(JOIN)
  • 考慮使用臨時表或表變量存放中間結果
  • 少用子查詢
  • 視圖嵌套不要過深,通常視圖嵌套不要超過2個爲宜。
  • 對出如今where子句中的字段加索引
  • 避免在索引列上使用函數或計算,在where子句中,若是索引是函數的一部分,優化器將再也不使用索引而使用全表掃描
  • 在insert和update維表時都加上一個條件來過濾維表中已經存在的記錄
  • 若是使用了IN或者OR等時發現查詢沒有走索引,使用顯式申明指定索引
  • EXISTS要遠比IN的效率高。

       ……….編程

 

問題出在哪了?

    雖然上述指導意見看上去沒什麼問題,也不能說徹底不正確,但實際上有兩個重大問題:安全

    脫離上下文:不少道理只能在一個上下文範圍內生效,脫離了上下文範圍就毫無心義。舉個例子,日常有人對你說你有點腎虛,我想你的第一反應確定是想辦法捍衛男人的尊嚴了,但若是你去醫院檢查醫生這麼說,那你可能就會一臉虔誠的求教如何補了:-),那舉上述摘錄的語句例子:1)少用子查詢,若是在SQL Server操做XML的XPATH按節點屬性篩選的時候,那轉換成子查詢必定會更快 2)若是使用了IN或者OR等時發現查詢沒有走索引,使用顯式申明指定索引,這種狀況查詢分析器不走索引必定會有其緣由,編程語言

 

    不解釋本質緣由:佛語有云「凡全部相,皆是虛妄,若見諸相非相,即見如來」。請看下面故事:函數

說有一次兩個府吏一塊兒來看病,一個叫倪尋,一個叫李延,兩人的症狀也同樣,都是頭痛,身上發熱,也許都是感冒吧。而華佗卻說:「倪尋應當用下法來治,李延應當用汗法來治(尋當下之,延當發汗)。」旁人認爲很奇怪,你們也必定認爲很奇怪吧,爲何一樣的一個病,一樣的症狀,會有不一樣的治療法子呢?華佗解釋了,他說:「倪尋是外實,而立延是內實,因此用了不一樣的法子。」果真,次日,他們兩的病都好了。性能

    其實能夠看出,徹底一樣的症狀,能夠是徹底不一樣的緣由,反之,一樣的緣由,也能夠造成徹底不一樣的「相」。若是僅僅是看到「相」而採起應激處理措施,每每結果會不盡人意。優化

 

Think Like Query Optimizer

    在每個領域都有其領域內的規則,最簡單來講,若是你不符合C#規範去編程,好比錯誤的使用關鍵字,那麼編譯就會報錯。固然,每個領域內還會有一些隱藏的規則,也有人會說是所謂的「潛規則」,這類規則每每不在明面上,好比說你不符合最佳實踐編寫一段程序,編譯不會報錯,但所以而引發的性能或是安全性問題就是你須要遵循最佳實踐這個「潛規則」才能避免。blog

    而在SQL Server領域,T-SQL語句到查詢結果返回須要經歷一個完整的週期,如圖1:索引

    image

    圖1.T-SQL生命週期

 

    所以,在關係數據庫領域,SQL語句的寫法只是一個抽象的邏輯,而不是像編程語言那樣直接的實現。好比說訪問一行數據,若是是編程語言實現,就須要指定鏈接數據的方式,打開數據,按某個方式取出數據,最後還要關閉鏈接,而在SQL Server中,T-SQL僅僅是定義如何去獲取所需的數據,而無需考慮實現細節。

    圖1中從T-SQL到具體返回數據經歷了多個步驟,每個步驟又存在大量的規則。所以在本文提到Where 1=1 and引發的性能問題就須要按照查詢分析器的規則去考慮爲何,這也是Think like query optimizer。

 

    在SQL Server中,T-SQL須要編譯爲執行計劃才能去執行,在編譯過程當中,Query Optimizer須要考慮不少元數據,好比說表上的索引、數據分佈、估計行數、一些參數配置、硬件環境等,在這其中,最重要的就是估計行數,SQL Server須要估計行數來估計成本。

 

Where 1=1 and寫法爲何不會變慢?

    由於查詢分析器在代數樹優化階段就把1=1 直接給過濾掉了。這個功能就是查詢優化器中所謂的「Constant Folding」。

    咱們這裏假設查詢分析器在代數樹優化階段沒有把where 1=1這種狀況直接過濾掉。

    好比語句select * from table where a=1 and b=2 這個語句,SQL Server估計的行數會是:

    a列的選擇率*b列的選擇率*表中採樣的總行數

    所以,當Where 1=1 and a=1時,結果就變爲

     1*a列的選擇率 *表中採樣的總行數=a列的選擇率 *表中採樣的總行數

 

    所以不管是否有1=1 and,查詢分析器都會估計相同的行數,從而擁有一樣的執行計劃,所以不影響性能。

 

    當咱們明白了查詢分析器對A and B這種寫法是如何估計行數以後,那麼咱們就能夠推算出什麼狀況A and B可能引發執行計劃不許確。從公式來看,SQL Server認爲A列和B列是無關聯的,若是A和B關聯很大,那麼估計的行數必定會很是不許。

    這裏咱們舉例,假如表中有100萬行數據,where a=1的數據有1萬條,where b=1的數據有1萬條,則A和B的選擇性都是1/100=0.01,在Where中A And B聯合的估計行數則變爲0.01*0.01=0.0001*100萬=100行,假設where a=1 和b=1所篩選的數據爲一樣的1萬行數據,則估計行數爲100而實際行數爲1萬,則可能引發執行計劃的不許確,從而引發性能問題。固然,這種狀況的確是少數,但發生後每每對性能有必定影響,所以SQL Server 2014新的行數估計採用了指數退讓算法,在這種狀況下就會估計爲1000行,從而引發性能問題的可能性會變小,2014指數退讓算法不是本文的重點,所以也很少講了。

相關文章
相關標籤/搜索