結論先行的話就是: 對於相對來說簡單的SQL,Inner join的方式過濾和放在Where條件中過濾性能上來說是一樣的, 但是對於復雜的SQL,有可能出現Inner join過濾出現性能問題的情況,此時可以嘗試將條件放在where中做嘗試,這個問題沒有什么定論
前段時間遇到一個存儲過程,參數之一是一個字符串,在存儲過程中,把字符串拆分成一個臨時表之后存為一個key值的臨時表,作為其中一個查詢條件, 邏輯實現上有兩種處理方式
insert into #t select key from split_function('傳遞進來的字符串',',')
第一種是與物理表做inner join,類似如下
select * from tableA a inner join tableB b on a.id = b.id inner join #t c on b.key=c.key inner join otherTables d on a.id=d.id where a.column2 between @paramater1 and @parameter2 and other query condition
第二種是將這個過濾條件放在where 條件中
select * from tableA a inner join tableB b on a.id = b.id inner join otherTables c on a.id=c.id where a.column between @paramater1 and @parameter2 and b.key in (select key from #t) and other query condition
實際上這個存儲過程本身比較復雜,十多張表的一個復雜的join和多鍾過濾邏輯,其中有幾張大表將近千萬級,核心點的不同在於類似上面查詢條件的處理方式, 本身的邏輯是用第一種方式去實現的,因為有較大的性能問題,一開始把重點放在索引,統計信息之類上面,怎么也找不到原因 發現在性能始終很差,上面我只是舉一個簡單的例子,實際情況遠遠比上面復雜,上很難去模擬實際的邏輯
這個問題困惑了我好久, 因為當時沒意識到上述第一種寫法下的用inner join中間結果集的方式過濾和直接放在where 條件中的區別, 后來仔細觀察執行計划,發現第一種方式的執行計划是這樣的: 執行計划最開始對物理表做過濾的時候,沒有先用#t中的值去過濾物理表,僅僅用TableA上column2 的過濾條件得到一個結果集,然后用這個較大的結果集去驅動其他表,最后再去跟#t做join, 等於是中間結果集非常大,最后才去跟#t做join過濾,性能上比較差
由於當時以來沒意識到時上述inner join #t的方式造成的,把問題集中在索引上,對索引,統計信息之類的做各種分析優化,都沒有得到怎么改善 后來換成第二種方式,效率提高了很多, 生成的圖形化的執行計划,估計兩屏都顯示不下,通過一步一步的觀察, 看了好久,才發現是兩種方式的差別在於這里: 對於物理表的處理是這樣的:用上column2 和 #t 與TABLEB的結果共同去過濾TableA,得到一個中間結果集,然后去驅動其他的表 雖然最后的結果是一樣的,但是這個查詢的效率差別非常大,因為一開始對TableA過濾的時候,得到的是一個比較小的結果,后面再去驅動其他表或者是跟其他表join, 由於這里生成的中間結果集事先利用了#t的過濾條件,所以中間結果集比較小, 因此后繼跟其他表join起來,整體代價比較小,性能上有一個較大的提升
這個問題困惑了好多天,本來想自己寫個demo驗證一下的,無奈實際場景太復雜了,很難模擬出那種數據和表之間的邏輯關系。 不過可以明確的是,上述寫法,對於簡單的demo,可能性能上區別不大,但是執行計划的差別還是很明顯的,對於復雜的情況,可能就要具體分析了 以后優化sql的時候,多個思路,尤其是在復雜的條件下,面對查詢條件的處理方式,是否有必要用inner join的方式達到過濾數據的目的,一定要慎重。
最后上兩個簡單的demo,看一下執行計划的區別,實際情況是在是模擬不出來,只能這樣通過執行計划看一下兩者之間的區別 截圖可以看到,上述demo中的兩種寫法,分別用Inner join和Where處理一個表中的數據,邏輯上是一樣的,但是在執行計划上並不是完全一樣的,如果執行計划不一樣,性能上肯定有差別 這里只能建議,遇到類似問題,嘗試改寫SQL,看看默認情況下執行計划是否會發生變化,從而選擇一種性能較好的寫法。 (本文不討論索引,僅僅在相同的表結構情況下,從執行計划差異的角度來看問題的)