4

両方に対してクエリを実行している2つのテーブルには、それぞれ最大1億5,000万行があります。

次のステートメントは、45分間戻らなかった後に終了するため、実行時間がわかりません。

select * from Cats cat  
where not exists( select dog.foo,dog.bar from Dogs dog
                  where cat.foo = dog.foo   
                  and cat.bar = dog.bar);

ただし、このクエリは約3分で実行されます。

select * from Cats outside  
   where not exists(select * from Cats cat  
                     where exists( select dog.foo,dog.bar from Dogs dog
                      where cat.foo = dog.foo   
                      and cat.bar = dog.bar)));

私の質問は、このパフォーマンスの向上が見られる舞台裏で何が起こっているのかということです。

同じ結果セットを返す理由:

最初のクエリ(遅い)は、Catsテーブルに基づいて存在しないすべての要素を提供することを示しています。

2番目のクエリ(高速)は、存在する猫のサブセットから存在しないすべての要素を提供することを示しています。

私は次のクエリを期待しています:

select dog.foo,dog.bar from Dogs dog
                          where cat.foo = dog.foo   
                          and cat.bar = dog.bar  

[A、B、C]を返す

これは両方の機能に共通です。

私の猫のテーブルには次のものがあります:[A、B、C、D、E]

私は次のクエリを期待します:

 select * from Cats cat  
                     where exists

[A、B、C]と最後のピースを返すには:

select * from Cats outside  
       where not exists

[D、E]を返す

アップデート

私の主張を数学的に証明するために記法を設定します(間違った記号を使用した場合は訂正してください):

∀ Cat (Ǝ cat ≠ Ǝdog)    

Catのすべての要素について、dogの要素と等しくないcatの各要素を含むセットを返します。

∀ Cat (Ǝ cat = Ǝdog)   

Catのすべての要素について、dogの要素と等しいcatの各要素を含むセットを返します。

∀ Cat (Ǝ innerCat ≠ Ǝcat)  

Catのすべての要素について、catの要素と等しくない内部catの各要素を含むセットを返します。

2回目の更新

私の数学が私のSQLと一致していなかったことがわかります。

4

5 に答える 5

3

どうやら、NOTINとNOTEXISTSは、データエンジンが最適化するのに問題があります。技術的には、これらは反結合と呼ばれます(等結合、半結合、非等結合などとは区別されます)。

結合を最適化するのが難しい場合、エンジンはネストされたループ結合に頼ります。これらは通常、パフォーマンスが最も低い種類です(ただし、SQL Serverの実行プランでは、SQL Serverがインデックスルックアップを実行プランの「ネストされたループ」で検索するため、これらは同じように見えることがよくあります)。

これら2つのクエリの違いは何ですか?最初のものは存在しないだけなので、おそらく非効率的なことをしています。2つ目は、最も内側のサブクエリでEXISTSを実行することです。これは、基本的に結合として、最初に最適化されます。キーにインデックスがあれば、すべて問題ありません。SQL Serverは、これらに対してハッシュベースまたはマージベースのアルゴリズムを選択することもできます。

2番目のバージョンの「存在しない」は同じテーブルに基づいています。これにより、SQLServerに最適化の余地が増える可能性があります。

最後に、2番目のバージョンでは、データセットが大幅に削減されている可能性があります。もしそうなら、外側のネストされたループ結合でさえ、はるかに速くなるかもしれません。

于 2012-08-03T14:24:02.747 に答える
2

2番目のクエリは実行時にはるかに最適であり、これが理由です。

外部クエリのCatsテーブルをにエイリアスしますが、の句でoutside参照しません。したがって、SQLは次のことを実行できます。outsidewhere not exists

  • cat.foo = dog.foo and cat.bar = dog.bar(最も内側のクエリから)どこにでも猫を1匹見つける
  • これは、すべての猫のためにあなたの外側に会う猫が存在することを意味しますwhere not exists outside
  • したがって、このwhere not exists句はfalseのすべての行に適用されますoutside
  • したがって、クエリの結果は空になります

最初のクエリは、テーブル内のすべての猫に対してネストされたクエリを再実行する必要があるため、処理が遅くなります。

于 2012-08-03T14:28:48.580 に答える
1

あなたの質問に対する答えは、実行計画を確認することです。

補足として、この同等のクエリを試す必要があります(https://stackoverflow.com/a/1069467/44522も参照):

SELECT * FROM Cats cat LEFT OUTER JOIN Dogs dog 
    ON cat.foo = dog.foo and cat.bar = dog.bar
WHERE dog.foo IS NULL and dog.bar IS NULL

私はそれがはるかに速く実行されるに違いない(あなたが正しいインデックスを持っていると仮定して)。

于 2012-08-03T14:22:45.107 に答える
0

テストを通じて、これが最初の質問でクエリを実行する最も効率的な方法であることがわかりました。

Select cat.foo,cat.bar from cats cat

MINUS

Select dog.foo,dog.bar from dogs dog

私の列はどれもnull許容ではないため、これは機能します。

于 2012-08-07T12:58:17.010 に答える
-1

それらは異なるクエリであり、結果も異なります。2番目のリターンを最初のリターンと同じにするには、次のようにする必要があります...

   select * from cats outside   
   where not exists(select * from Cats cat   
                     where exists( select dog.foo,dog.bar from Dogs dog 
                      where cat.foo = dog.foo    
                      and cat.bar = dog.bar)
                      and outside.foo = cat.foo
                      and outside.bar=cat.bar
                      ) 
于 2012-08-03T14:17:24.993 に答える