web-dev-qa-db-ja.com

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

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内でクエリを使用しています。

72

最大の違いは、結合にないか、存在しないかです(記述どおり)、SELECT *

最初の例では、bothABからすべての列を取得しますが、2番目の例では、 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);

実行計画:

enter image description here

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

60
Max Vernon

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

何年も前(SQL Server 6.0 ish)の場合、LEFT JOINの方が高速でしたが、これは長い間当てはまりませんでした。最近では、NOT EXISTSはわずかに高速です。


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

25
Rob Farley

NOT EXISTSLEFT JOIN ... WHERE IS NULLよりも(わずかに)優れていることに気付いた例外は、リンクサーバーを使用する場合です。

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

この動作を示す実行プランの例: enter image description here

7
pimbrouwers

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

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

(4)の場合:

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

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

ただし、これは重み付けされた入力のような単なる「励まし」です。強い(1)、(2)、(3)は、(4)の根拠のない選択をします。

(@MaxVernonの回答で対処されている*のために異なる列を返す例の影響を無視します。).

5
crokusek