これらのクエリのどれが速いですか?
存在しません:
SELECT ProductID, ProductName
FROM Northwind..Products p
WHERE NOT EXISTS (
SELECT 1
FROM Northwind..[Order Details] od
WHERE p.ProductId = od.ProductId)
またはではない:
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
バージョンはさらに作業を行う必要があります(実際にNULL
sが存在しない場合でも)およびNULL
sareが存在する場合のNOT IN
のセマンティクスは、とにかく必要なものではありません。
Products.ProductID
も[Order Details].ProductID
もNULL
sを許可しない場合、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
sが含まれる場合、結果を返さないという正しいセマンティクスのためです。計画に追加されていることを確認するには、追加の反半結合および行カウントスプールを参照してください。
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)
その理由は、Products.ProductId
サブクエリが結果を返さない場合、結果にNULL
NOT IN
を返すべきではないためですexceptまったく(つまり、[Order Details]
テーブルは空です)。その場合、そうすべきです。サンプルデータの計画では、以下のように別の反準結合を追加することでこれを実装しています。
この効果は、 Buckleyによって既にリンクされているブログ投稿に示されています 。この例では、論理読み取りの数が約400から500,000に増加します。
さらに、単一のNULL
で行カウントをゼロに減らすことができるという事実により、カーディナリティの推定が非常に困難になります。これが起こるとSQL Serverが想定しているが、実際にデータにNULL
行がなかった場合、これがより大きなクエリの一部である場合、残りの実行計画は壊滅的に悪化する可能性があります、 不適切なネストされたループたとえば、高価なサブツリーの繰り返し実行を引き起こす 。
ただし、これはNULL
- able列のNOT IN
の唯一の可能な実行プランではありません。 この記事は別の記事を示しますAdventureWorks2008
データベースに対するクエリの場合。
NOT IN
列のNOT NULL
、またはNULL入力可能またはNULL入力不可の列に対するNOT EXISTS
の場合、次の計画を示します。
列がNULL
- ableに変わると、NOT IN
プランは次のようになります。
プランに余分な内部結合演算子を追加します。この装置は ここで説明されています 。 Sales.SalesOrderDetail.ProductID = <correlated_product_id>
での以前の単一の相関インデックスシークを、外側の行ごとに2つのシークに変換するのはすべてです。追加のものはWHERE Sales.SalesOrderDetail.ProductID IS NULL
にあります。
これはアンチセミジョインの下にあるため、行が返される場合、2番目のシークは発生しません。ただし、Sales.SalesOrderDetail
にNULL
ProductID
sが含まれていない場合、必要なシーク操作の数が2倍になります。
また、NOT INは、NULLになるとNOT EXISTSと同じにはなりません。
この記事はそれを非常によく説明しています
http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/
サブクエリがNULLを1つでも返す場合、NOT INはどの行とも一致しません。
この理由は、NOT IN操作が実際に何を意味するのかの詳細を調べることによって見つけることができます。
説明のために、テーブル内にtという名前の4行があり、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はID = 4の場合NULLであるとします。したがって、!=比較ではUNKNOWNが返されます。 ANDの論理真理値表は、UNKNOWNとTRUEがUNKNOWN、UNKNOWNとFALSEがFALSEであることを示しています。結果がTRUEになるようにUNKNOWNとANDをとることができる値はありません
したがって、その副問合せのいずれかの行が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、400、200の行数を持つ他の4つのテーブルの中で存在しないもの(varcharカラムと一致する)だけを選択する必要があります。該当するVarchar
列に。
NOT IN
は約10分かかり、NOT EXISTS
は4秒かかりました。
私は10分に貢献したかもしれないいくつかの未調整セクションを持っているかもしれない再帰クエリを持っていますが、NOT EXISTS
ははるかに優れているか少なくともIN
とEXISTS
は正確に同じではないコードを読み進める前に必ずチェックする価値があります。
あなたの特定の例では、それらは同じです。オプティマイザはあなたがやろうとしていることが両方の例で同じであることを考え出したからです。しかし、自明ではない例ではオプティマイザがこれを実行しない可能性があります。その場合は、場合によっては他を優先する理由があります。
外側のselectで複数の行をテストしている場合は、NOT IN
をお勧めします。 NOT IN
ステートメント内で副照会を実行するたびに副選択を再実行するのではなく、NOT EXISTS
ステートメント内の副照会を実行の開始時に評価し、一時表を外部選択の各値に対して検査することができます。
副照会mustが外部選択と相関している場合、オプティマイザーが同じ機能を実行するための一時表の作成を妨げる単純化を発見する可能性があるため、NOT EXISTS
が推奨されます。
私が使っていた
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を使い始めました。
それらは非常に似ていますが、実際には同じではありません。
効率の面では、 left joinがnull ステートメントの方が効率的です(豊富な行を選択する場合)
オプティマイザがそれらが同じであると言ったら、人的要因を検討してください。 NOT EXISTS :)を見るのが好きです。