私は常にデフォルトでNOT EXISTS
。
実行計画は現時点では同じである可能性がありますが、いずれかの列が将来変更されてNULL
が許可される場合 ■NOT IN
バージョンはより多くの作業を行う必要があります(NULL
がない場合でも) sは実際にデータに存在します)およびNOT IN
のセマンティクス NULL
の場合 ■ とにかく存在するものがあなたが望むものである可能性は低いです。
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
です -クエリが可能になります
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
sは結果を返しません。プランに追加されていることを確認するには、追加のアンチセミジョインおよび行カウントスプールを参照してください。
Products.ProductID
の場合 また、NULL
になるように変更されます -クエリが可能になります
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)
その理由は、NULL
Products.ProductId
結果に返されるべきではありません例外 NOT IN
の場合 サブクエリは結果をまったく返さないことでした(つまり、[Order Details]
テーブルは空です)。その場合はそうすべきです。私のサンプルデータの計画では、これは以下のように別のアンチセミジョインを追加することによって実装されています。
この効果は、Buckleyによってすでにリンクされているブログ投稿に示されています。この例では、論理読み取りの数が約400から500,000に増加しています。
さらに、単一のNULL
行数をゼロに減らすことができるため、カーディナリティの推定が非常に困難になります。 SQL Serverがこれが発生すると想定しているが、実際にはNULL
がなかった場合 データ内の行は、実行プランの残りの部分が壊滅的に悪化する可能性があります。これがより大きなクエリの一部であり、不適切なネストされたループが原因で、たとえば高価なサブツリーが繰り返し実行される場合などです。
NOT IN
で可能な実行プランはこれだけではありません NULL
で ただし、可能な列。この記事では、AdventureWorks2008
に対するクエリの別の記事を示します。 データベース。
NOT IN
の場合 NOT NULL
で 列またはNOT EXISTS
null許容列または非null許容列に対して、次の計画を立てます。
列がNULL
に変更されたとき -NOT IN
が可能 計画は次のようになります
プランに内部結合演算子を追加します。ここでは、この装置について説明します。 Sales.SalesOrderDetail.ProductID = <correlated_product_id>
で以前の単一の相関インデックスシークを変換するのはこれですべてです。 外側の行ごとに2つのシークに。追加のものはWHERE Sales.SalesOrderDetail.ProductID IS NULL
にあります 。
これは反半結合の下にあるため、1つが行を返す場合、2番目のシークは発生しません。ただし、Sales.SalesOrderDetail
の場合 NULL
は含まれていません ProductID
■必要なシーク操作の数が2倍になります。