LEFT JOINまたはNOT EXISTSの使用のベストプラクティス


67

LEFT JOINまたはNOT EXISTS形式を使用する間にベストプラクティスはありますか?

一方を他方より使用する利点は何ですか?

存在しない場合、どちらを優先すべきですか?

SELECT *
FROM tableA A
LEFT JOIN tableB B
     ON A.idx = B.idx
WHERE B.idx IS NULL

SELECT *
FROM tableA A
WHERE NOT EXISTS
(SELECT idx FROM tableB B WHERE B.idx = A.idx)

SQL Serverデータベースに対してAccess内でクエリを使用しています。


2
余談として、一見、同じアプローチがWHERE A.idx NOT IN (...) ありません、同一原因の三価の動作にNULL(つまり、NULL等しくないNULL)(でも等しくない、したがって、あなたが持っている場合はいずれかを NULLしてtableB、あなたの予期しない結果が得られます!)
Elaskanator

回答:


58

最大の違いは、結合と存在ではなく、(書かれているように)SELECT *です。

最初の例では、あなたからすべての列を取得し、両方 AB第二の例では、あなたから列のみを取得するのに対し、A

SQL Serverでは、2番目のバリアントは非常に単純な不自然な例でわずかに高速です。

2つのサンプルテーブルを作成します。

CREATE TABLE dbo.A
(
    A_ID INT NOT NULL
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
);

CREATE TABLE dbo.B
(
    B_ID INT NOT NULL
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
);
GO

各テーブルに10,000行を挿入します。

INSERT INTO dbo.A DEFAULT VALUES;
GO 10000

INSERT INTO dbo.B DEFAULT VALUES;
GO 10000

2番目のテーブルから5行ごとに削除します。

DELETE 
FROM dbo.B 
WHERE B_ID % 5 = 1;

SELECT COUNT(*) -- shows 10,000
FROM dbo.A;

SELECT COUNT(*) -- shows  8,000
FROM dbo.B;

2つのテストSELECTステートメントのバリアントを実行します。

SELECT *
FROM dbo.A
    LEFT JOIN dbo.B ON A.A_ID = B.B_ID
WHERE B.B_ID IS NULL;

SELECT *
FROM dbo.A
WHERE NOT EXISTS (SELECT 1
    FROM dbo.B
    WHERE b.B_ID = a.A_ID);

実行計画:

ここに画像の説明を入力してください

2番目のバリアントは、左の反準結合演算子を使用できるため、フィルター操作を実行する必要はありません。


23

論理的には同じですが、NOT EXISTSあなたが求めているAntiSemiJoinに近く、一般的に好まれます。また、Bの列にアクセスできないことも強調表示されます。これは、NULL値で使用できるようにするのではなく、フィルターとしてのみ使用されるためです。

何年も前(SQL Server 6.0っぽい)のLEFT JOIN方が速かったのですが、そうではありませんでした。最近でNOT EXISTSは、わずかに速くなっています。


Accessでの最大の影響は、JOINメソッドが結合をフィルタリングしてから結合セットをメモリに構築する前に結合を完了する必要があることです。これを使用NOT EXISTSすると、行がチェックされますが、列にスペースは割り当てられません。さらに、行が見つかると検索を停止します。Accessのパフォーマンスは多少異なりますが、一般的な経験則ではNOT EXISTS、少し速くなる傾向があります。より多くの要因が関係しているため、「ベストプラクティス」とは言いたくないでしょう。


6

Linked Serversを使用NOT EXISTSするLEFT JOIN ... WHERE IS NULL場合は、(わずかですが)優れていることに気づいた例外です。

実行計画を調べると、NOT EXISTS演算子はネストされたループ形式で実行されているようです。これにより、行ごとに実行されます(これは理にかなっていると思います)。

この動作を示す実行計画の例: ここに画像の説明を入力してください


1
リンクされたサーバーは、そのようなことに対して残忍です。この問題を解決するための可能なアプローチは、データベースの一時的なコピーに対して単純な句をINSERT INTO #t (a,b,c) SELECT a,b,c FROM LinkedServer.database.dbo.table WHERE x=y実行してから、リンクサーバーリンク経由でリモートデータをコピーするNOT EXISTS (...)ことです。
マックスヴァーノン

2
私の投稿でMax Vernonから返事をもらうのはちょっと恥ずかしいです!脇にファンボイ。おもしろいのは、そのクロスサーバーの状況を最大限に活用するために何度かその正確なアプローチを使用してきたことです。
ロボピム

1
乾杯、@ pimbrouwers-親切なコメントありがとう!
マックスヴァーノン

5

一般的に、エンジンは基本的に以下に基づいて実行計画を作成します。

  1. AおよびBの行数
  2. Aおよび/またはBにインデックスがあるかどうか
  3. 結果行(および中間行)の予想数
  4. 入力クエリの形式(つまり、質問)

(4)の場合:

「存在しない」プランは、テーブルBのシークベースのプランを推奨します。これは、テーブルAが小さく、テーブルBが大きい(およびインデックスがBに存在する)場合に適した選択肢です。

「antijoin」プランは、テーブルAが非常に大きいか、テーブルBが非常に小さいか、Bにインデックスがなく、大きな結果セットを返す場合に適しています。

しかし、それは、加重入力のような単なる「励まし」です。強い(1)、(2)、(3)は、多くの場合、(4)論争の選択をします。

(*のために異なる列を返す例の効果を無視し、@ MaxVernon回答で対処します。)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.