sql >> データベース >  >> RDS >> Sqlserver

NOT IN vs NOT EXISTS

    私は常にデフォルトで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倍になります。



    1. PostgreSQL9.0をWindowsサービスとして登録して実行します

    2. PostgreSQLのLEAST()関数

    3. SQL Serverデータベース変更リスナーC#

    4. 新しいSQLPerformance.comニュースレター