這些查詢中哪一個更快? php
不存在: html
SELECT ProductID, ProductName FROM Northwind..Products p WHERE NOT EXISTS ( SELECT 1 FROM Northwind..[Order Details] od WHERE p.ProductId = od.ProductId)
或不在: web
SELECT ProductID, ProductName FROM Northwind..Products p WHERE p.ProductID NOT IN ( SELECT ProductID FROM Northwind..[Order Details])
查詢執行計劃說他們都作一樣的事情。 若是是這樣,建議使用哪一種形式? sql
這基於NorthWind數據庫。 數據庫
[編輯] 併發
剛剛發現這篇有用的文章: http : //weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx ide
我想我會堅持不存在。 優化
還應注意,當爲空時,NOT IN不等於NOT EXISTS。 spa
這篇文章很好地解釋了 code
http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/
當子查詢甚至返回一個null時,NOT IN將不匹配任何行。
能夠經過查看NOT IN操做實際含義的詳細信息來找到其緣由。
假設出於說明目的,表中有4行稱爲t,其中有一列名爲ID的值爲1..4
WHERE SomeValue NOT IN (SELECT AVal FROM t)至關於
WHERE SomeValue != (SELECT AVal FROM t WHERE ID=1) AND SomeValue != (SELECT AVal FROM t WHERE ID=2) AND SomeValue != (SELECT AVal FROM t WHERE ID=3) AND SomeValue != (SELECT AVal FROM t WHERE ID=4)進一步說,AVal爲NULL,其中ID =4。所以,!=比較返回UNKNOWN。 AND的邏輯真值表指出UNKNOWN和TRUE爲UNKNOWN,UNKNOWN和FALSE爲FALSE。 沒有值能夠與UNKNOWN進行與運算以產生結果TRUE
所以,若是該子查詢的任何行返回NULL,則整個NOT IN運算符將求值爲FALSE或NULL,而且將不返回任何記錄
我老是默認爲NOT EXISTS
。
目前的執行計劃可能相同,可是若是未來更改任一列以容許NULL
則NOT IN
版本將須要作更多的工做(即便數據中實際上沒有NULL
),而且不管如何,若是存在NULL
則 NOT IN
不太多是您想要的。
當Products.ProductID
或[Order Details].ProductID
不容許NULL
將NOT IN
與如下查詢等同對待。
SELECT ProductID, ProductName FROM Products p WHERE NOT EXISTS (SELECT * FROM [Order Details] od WHERE p.ProductId = od.ProductId)
確切的計劃可能會有所不一樣,可是對於個人示例數據,我獲得如下信息。
一個合理的廣泛誤解彷佛是與聯接相比,相關的子查詢老是「很差的」。 當他們強制執行嵌套循環計劃(逐行評估子查詢)時,固然能夠,可是該計劃包括反半聯接邏輯運算符。 反半聯接不限於嵌套循環,還可使用哈希或合併(如本例所示)聯接。
/*Not valid syntax but better reflects the plan*/ SELECT p.ProductID, p.ProductName FROM Products p LEFT ANTI SEMI JOIN [Order Details] od ON p.ProductId = od.ProductId
若是[Order Details].ProductID
是NULL
-able查詢就變成
SELECT ProductID, ProductName FROM Products p WHERE NOT EXISTS (SELECT * FROM [Order Details] od WHERE p.ProductId = od.ProductId) AND NOT EXISTS (SELECT * FROM [Order Details] WHERE ProductId IS NULL)
這樣作的緣由是,若是[Order Details]
包含任何NULL
ProductId
,則正確的語義將不返回任何結果。 請參閱額外的反半鏈接和行計數假脫機以驗證是否已將其添加到計劃中。
若是Products.ProductID
也更改成NULL
-able,則查詢變爲
SELECT ProductID, ProductName FROM Products p WHERE NOT EXISTS (SELECT * FROM [Order Details] od WHERE p.ProductId = od.ProductId) AND NOT EXISTS (SELECT * FROM [Order Details] WHERE ProductId IS NULL) AND NOT EXISTS (SELECT * FROM (SELECT TOP 1 * FROM [Order Details]) S WHERE p.ProductID IS NULL)
這樣作的緣由是, 除非 NOT IN
子查詢根本不返回任何結果(即[Order Details]
表爲空), 不然不該在結果中返回NULL
Products.ProductId
。 在這種狀況下應該。 在個人樣本數據計劃中,這是經過添加另外一個反半聯接來實現的,以下所示。
Buckley已經連接的博客文章中顯示了這種效果。 在該示例中,邏輯讀取的數量從大約400增長到500,000。
另外,單個NULL
能夠將行計數減小到零這一事實使基數估計很是困難。 若是SQL Server假定會發生這種狀況,但實際上數據中沒有NULL
行,則執行計劃的其他部分可能會災難性地惡化,若是這只是較大查詢的一部分, 而且嵌套循環不當會致使重複執行昂貴的查詢例如子樹 。
可是,這不是可爲NULL
列上的NOT IN
惟一可行的執行計劃。 本文顯示了另外一個針對AdventureWorks2008
數據庫的查詢。
對於NOT NULL
列上的NOT IN
或針對可爲空或不可爲空的列的NOT EXISTS
,它給出瞭如下計劃。
當列更改成NULL
-able時, NOT IN
計劃如今看起來像
它將額外的內部聯接運算符添加到計劃中。 這裏說明該裝置。 只須要將Sales.SalesOrderDetail.ProductID = <correlated_product_id>
上的單個相關索引搜索轉換爲每一個Sales.SalesOrderDetail.ProductID = <correlated_product_id>
兩個搜索。 另外一個在WHERE Sales.SalesOrderDetail.ProductID IS NULL
。
因爲這是在反半聯接下,若是該聯接返回任何行,則不會發生第二次尋道。 可是,若是Sales.SalesOrderDetail
不包含任何NULL
ProductID
它將使所需的查找操做次數增長一倍。
我在用
SELECT * from TABLE1 WHERE Col1 NOT IN (SELECT Col1 FROM TABLE2)
並發現它給出了錯誤的結果(錯誤是指沒有結果)。 因爲TABLE2.Col1中爲NULL。
將查詢更改成
SELECT * from TABLE1 T1 WHERE NOT EXISTS (SELECT Col1 FROM TABLE2 T2 WHERE T1.Col1 = T2.Col2)
給了我正確的結果。
從那時起,我開始在每一個地方都使用NOT EXISTS。
若是執行計劃者說他們是相同的,那麼他們是相同的。 使用任何一種都會使您的意圖更加明顯-在這種狀況下,使用第二種。
這取決於..
SELECT x.col FROM big_table x WHERE x.key IN( SELECT key FROM really_big_table );
不會相對較慢,也沒有太大的限制來限制查詢檢查以查看它們是否鍵入的大小。在這種狀況下,最好使用EXISTS。
可是,這取決於DBMS的優化器,可能沒有什麼不一樣。
以EXISTS什麼時候更好爲例
SELECT x.col FROM big_table x WHERE EXISTS( SELECT key FROM really_big_table WHERE key = x.key); AND id = very_limiting_criteria