本節咱們來分析LEFT JOIN和NOT EXISTS,簡短的內容,深刻的理解,Always to review the basics。sql
以前咱們已經分析過IN查詢在處理空值時是基於三值邏輯,只要子查詢中存在空值此時則沒有任何數據返回,而LEFT JOIN和NOT EXISTS不管子查詢中有無空值上處理都是同樣的,固然比較重要的是利用LEFT JOIN...IS NULL來檢查NULL。基於兩者返回的結果集是同樣的,下面咱們開始直接用前面節所建立表來進行測試。在BigTable和SmallerTable上首先未建立索引性能
USE TSQL2012 GO DBCC FREEPROCCACHE DBCC DROPCLEANBUFFERS SET STATISTICS IO ON SET STATISTICS TIME ON SELECT BigTable.ID, SomeColumn FROM BigTable LEFT OUTER JOIN SmallerTable ON BigTable.SomeColumn = SmallerTable.LookupColumn WHERE LookupColumn IS NULL SELECT ID, SomeColumn FROM BigTable WHERE NOT EXISTS (SELECT LookupColumn FROM SmallerTable WHERE SmallerTable.LookupColumn = BigTable.SomeColumn)
兩者執行CPU Time和elapsed Time以下學習
咱們看到上述查詢計劃未建立索引以前兩者在開銷上接近一致,而LEFT JOIN....IS NULL則首先進行哈希匹配中的右外部聯接,而後就是過濾,換句話說是LEFT JOIN....IS NULL會直接徹底JOIN,而後再對重複數據進行過濾,而NOT EXISTS則是直接利用哈希匹配中的右半聯接,關於半聯接咱們在前面也已經說過,此時如有重複數據直接只取一個。因此LEFT JOIN....IS NULL和NOT EXISTS兩者對於重複數據一個經過兩部操做完成先徹底JOIN後進行過濾,而另一個則是直接經過右半聯接過濾。因此對於此兩者最大的不一樣在於:當使用LEFT JOIN.....IS NULL時,SQL尚未那麼聰明,僅僅只檢查一次,所以它須要經過徹底JOIN和過濾來完成,而NOT EXISTS則是在JOIN時就進行過濾。測試
在看兩者執行CPU TIME和elapsed TIME時間,沒有太大的差別。接下來咱們再來建立索引看看。優化
CREATE INDEX idx_BigTable_SomeColumn
ON BigTable (SomeColumn)
CREATE INDEX idx_SmallerTable_LookupColumn
ON SmallerTable (LookupColumn)
看看兩者的查詢執行計劃spa
此時咱們經過看到上述查詢執行計劃,咱們可以清楚的看到LEFT JOIN....IS NULL仍是徹底JOIN而後在過濾,只是建立了索引以後性能改善了一點而已,可是不一樣於LEFT JOIN...IS NULL的NOT EXISTS的計劃執行狀況不一樣於未建立索引,此時首先利用了流聚合而後哈希匹配中的右半聯接變成了合併聯接中的右半聯接,咱們一個個來看,這個Stream Aggregate(流聚合)是什麼鬼,對於此流聚合我是不瞭解的,不能裝懂,咱們接下來具體講講流聚合,至於爲何每當查詢計劃出現一個新的名詞都要去詳細瞭解下的緣由,相信看過我SQL Server本系列的童鞋知道,每一節的內容都很是短,不會出現閱讀疲勞,並且是精講,我重頭系統學習SQL Server是爲了對SQL Server中全部涉及到對性能調優有關的地方以及一些基礎知識都會去過一遍,以便後續再出現性能調優不至於一籌莫展。好了,回到話題,咱們看看Stream Aggregate。code
msdn上有關概念以下:Stream Aggregate運算符按一列或多列對行分組,而後計算查詢返回的一個或多個聚合表達式。此運算符的輸出可供查詢中的後續運算符引用和/或返回到客戶端。Stream Aggregate 運算符要求輸入在組中按列進行排序。若是因爲前面的 Sort 運算符或已排序的索引查找或掃描致使數據還沒有排序,優化器將在此運算符前面使用一個 Sort 運算符。在 SHOWPLAN_ALL 語句或 SQL Server Management Studio 的圖形執行計劃中,GROUP BY 謂詞中的列會列在 Argument 列中,而聚合表達式列在 Defined Values 列中。 blog
經過上述定義僅僅只是知道Stream Aggregate是用對行或者列進行聚合,至於何時在查詢計劃中出現流聚合,何時利用流聚合來提升查詢性能都是不得而知,咱們接下來一塊兒探討下。上述着重在於【分組】而後進行【聚合】計算,基於這點咱們來看看使用Stream Aggregate的三種場景。排序
USE TSQL2012
GO
SELECT COUNT(custid) AS cutid, SUM(empid) AS empid
FROM Sales.Orders
USE TSQL2012
GO
SELECT custid, COUNT(custid) AS countCustId
FROM Sales.Orders
GROUP BY custid
USE TSQL2012
GO
SELECT DISTINCT custid
FROM Sales.Orders
上述查詢使用經過DISTINCT,其實是對cutid進行了分組。以上是用到了Stream Aggregate的場景,固然聚合還有另一種就是哈希匹配聚合,後續會再進行補充。咱們再來理解Stream Aggregate定義,咱們將定義歸納爲對輸入進行排序後,接下來進行分組而後再進行聚合計算。在上述(2)和(3)中都是進行了分組,可是沒有排序,實際上內部已經默認實現了排序,咱們看下在(3)中表中custid數據,以下索引
當進行DISTINCT以後
可是在(3)中沒有進行聚合,爲何會進行流聚合呢?實際上在流聚合中存在狀態變量,狀態變量具體個數根據聚合個數而定,此狀態變量用來設置結果集,當進行分組後對應的數據進行保存,此時對應的狀態變量爲0,當匹配到對應數據時此時狀態變量加1,因此上述(3)中能夠說隱式進行了聚合計算,只是每條數據對應的狀態變量爲0而已,到了這裏就不難解釋,只進行了排序,分組而沒有進行聚合計算的緣由。關於Stream Aggregate都知道的一個例子則是咱們在利用SqlDataReader記性讀取數據時,能夠說是讀取流記錄,若是咱們須要彙總結果集時,此時每當Read時,其內部的狀態變量都會加1最終返回彙總和到客戶端。在這裏咱們只是簡單講講Stream Aggregate,後續會一併講講Hash Aggregate。咱們繼續回到LEFT JOIN....IS NULL和NOT EXISTS話題,當咱們建立索引以後此時LEFT JOIN....IS ISNULL執行時間是NOT EXISITS的兩倍多。到此,關於LEFT JOIN...IS NULL和NOT EXISTS就此結束,咱們一樣下個基本結論。
LEFT JOIN...IS NULL和NOT EXISTS性能分析結論:當咱們須要找到子查詢中不匹配的行而且列爲可空時,此時用NOT EXISTS,當須要找到子查詢中不匹配的行,此時列不爲空時能夠用NOT EXISTS或者NOT IN。
因爲LEFT JOIN..IS NULL對於不匹配的行不會當即進行返回而先須要徹底JOIN後過濾,尤爲是當有多個條件時,LEFT JOIN...IS NULL可能會更加影響查詢性能。
本節咱們學習了LEFT JOIN..IS NULL和NOT EXISTS的性能分析,下節咱們進入這幾節內容的綜合篇,綜合比較NOT IN VS NOT EXISTS VS LEFT JOIN...IS NULL終極篇。簡短的內容,深刻的理解,咱們下節再會。