質問
これらのクエリのどれが速いですか?
存在しない:
SELECT ProductID, ProductName
FROM Northwind..Products p
WHERE NOT EXISTS (
SELECT 1
FROM Northwind..[Order Details] od
WHERE p.ProductId = od.ProductId)
またはNOT NOT IN:
SELECT ProductID, ProductName
FROM Northwind..Products p
WHERE p.ProductID NOT IN (
SELECT ProductID
FROM Northwind..[Order Details])
クエリ実行プランでは、両方とも同じことを行うと書かれています。その場合、推奨される形式はどれですか?
これはNorthWindデータベースに基づいています。
[編集]
この役立つ記事が見つかりました: http://weblogs.sqlteam.com/mladenp/archive/2007 /05/18/60210.aspx
NOT EXISTSに固執すると思います。
解決
デフォルトは常にNOT EXISTS
です。
実行計画は現時点では同じかもしれませんが、いずれかの列が将来NULL
sを許可するように変更された場合、NOT IN
バージョンは(Products.ProductID
sが実際に存在しない場合でも)さらに作業を行う必要があります[Order Details].ProductID
s が存在する場合の[Order Details]
のセマンティクスは、とにかく必要なものではありません。
ProductId
もProducts.ProductId
もAdventureWorks2008
sを許可しない場合、NOT NULL
は次のクエリと同様に処理されます。
SELECT ProductID,
ProductName
FROM Products p
WHERE NOT EXISTS (SELECT *
FROM [Order Details] od
WHERE p.ProductId = od.ProductId)
正確な計画は異なる場合がありますが、私の例のデータでは次のようになります。
かなり一般的な誤解は、相関サブクエリは常に<!> quot; bad <!> quot;結合と比較して。ネストされたループプラン(行ごとに評価されるサブクエリ)を強制する場合もありますが、このプランには反準結合論理演算子が含まれます。アンチセミジョインはネストされたループに制限されませんが、ハッシュまたはマージ(この例のように)ジョインも使用できます。
/*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
Sales.SalesOrderDetail.ProductID = <correlated_product_id>
がWHERE Sales.SalesOrderDetail.ProductID IS 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)
この理由は、Sales.SalesOrderDetail
にProductID
<=> sが含まれる場合の正しいセマンティクスは結果を返さないことです。計画に追加されていることを確認するには、追加の反半結合および行カウントスプールを参照してください。
<=>も変更されて<=>-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)
その理由は、<=>サブクエリが結果をまったく返さない場合(つまり、<=> <=>が結果に except で返されないためです。 =>テーブルが空です)。その場合、そうすべきです。サンプルデータの計画では、以下のように別の反準結合を追加することでこれを実装しています。
この効果は、 Buckleyによって既にリンクされているブログ投稿。この例では、論理読み取りの数が約400から500,000に増加しています。
さらに、単一の<=>で行数をゼロに減らすことができるという事実により、カーディナリティの推定が非常に難しくなります。これが起こるとSQL Serverが想定しているが、実際にはデータに<=>行がなかった場合、これがより大きなクエリの一部である場合、実行計画の残りは壊滅的に悪化する可能性があります不適切なネストされたループにより、たとえば高価なサブツリーが繰り返し実行される。
ただし、これは<=>可能な列の<=>の唯一の実行プランではありません。 この記事は別の記事を示しています <=>データベースに対するクエリの場合。
<=>列の<=>、またはNULL入力可能またはNULL入力不可の列に対する<=>の場合、次のプランが提供されます。
列が<=>-ableに変わると、<=>プランは次のようになります
追加の内部結合演算子がプランに追加されます。この装置はここで説明されています。 <=>での以前の単一の相関インデックスシークを、外側の行ごとに2つのシークに変換するのはすべてです。追加のものは<=>にあります。
これが反セミ結合の下にあるため、行が返される場合、2番目のシークは発生しません。ただし、<=>に<=> <=>が含まれていない場合、必要なシーク操作の数が2倍になります。
他のヒント
また、NULLになるとNOT INはNOT EXISTSと同等ではないことに注意してください。
この投稿では非常によく説明されています
http://sqlinthewild.co.za/ index.php / 2010/02/18 / not-exists-vs-not-in /
サブクエリがnullを1つでも返す場合、NOT INはどれにも一致しません 行。
この理由は、何の詳細を調べることで見つけることができます NOT IN操作は実際に意味します。
Let <!>#8217;たとえば、説明のために、4つの行が tというテーブル、there <!>#8217; sという列に値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)
Let <!>#8217;さらに、AValはID = 4の場合NULLであると言います。したがって、!= 比較はUNKNOWNを返します。 AND状態の論理真理値表 UNKNOWNおよびTRUEはUNKNOWN、UNKNOWN、およびFALSEはFALSEです。がある AND <!>#8217; dとなる値はありません。UNKNOWNと組み合わせて、結果をTRUEにします
したがって、そのサブクエリのいずれかの行がNULLを返す場合、NOT IN全体 演算子はFALSEまたはNULLに評価され、レコードはありません 返された
実行プランナーが同じであると言った場合、それらは同じです。どちらを使用しても、意図がより明確になります。この場合は2番目です。
実際には、これが最速だと思います:
SELECT ProductID, ProductName
FROM Northwind..Products p
outer join Northwind..[Order Details] od on p.ProductId = od.ProductId)
WHERE od.ProductId is null
約120,000のレコードを持つテーブルがあり、行数が約1500、4000、40000、200である他の4つのテーブルに存在しない(varchar列と一致する)レコードのみを選択する必要があります。関係するVarchar
列に一意のインデックスがあります。
NOT IN
には約10分かかり、NOT EXISTS
には4秒かかりました。
10分に貢献したかもしれないチューニングされていないセクションがあるかもしれない再帰クエリがありますが、4秒かかる他のオプションは説明しています、少なくともIN
がはるかに優れているか、少なくともEXISTS
と<=>はまったく同じではなく、コードを実行する前に常に確認する価値があります。
あなたの特定の例では、オプティマイザはあなたがやろうとしていることを両方の例で同じであると考えているため、それらは同じです。しかし、自明ではない例ではオプティマイザーがこれを行わない可能性があり、その場合は時々他のものを好む理由があります。
外部選択で複数の行をテストする場合は、 NOT IN
をお勧めします。 NOT EXISTS
ステートメント内のサブクエリは、実行の開始時に評価でき、一時テーブルは、<=で必要になるたびにサブセレクトを再実行するのではなく、外側のselectの各値に対してチェックできます。 >ステートメント。
サブクエリを外部選択と相関させる必要がある場合、<=>が望ましい場合があります。これは、オプティマイザが、同じ機能を実行する一時テーブルの作成を妨げる単純化を発見する可能性があるためです。
使用していた
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の使用を開始しました。
これらは非常に似ていますが、実際は同じではありません。
効率の観点から、左結合がnull ステートメントの方が効率的であることがわかりました(つまり、大量の行を選択する場合)
オプティマイザーが同じであると言う場合は、人的要因を考慮してください。 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