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内でクエリを使用しています。
最大の違いは、結合にないか、存在しないかです(記述どおり)、SELECT *
。
最初の例では、bothA
とB
からすべての列を取得しますが、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);
実行計画:
2番目のバリアントは、左反準結合演算子を使用できるため、フィルター操作を実行する必要はありません。
論理的には同じですが、NOT EXISTS
は、求めているAntiSemiJoinに近く、一般的に推奨されます。また、Bの列にはアクセスできないことを強調しています。これは、(NULL値で使用できるようにするのではなく)フィルターとしてのみ使用されるためです。
何年も前(SQL Server 6.0 ish)の場合、LEFT JOIN
の方が高速でしたが、これは長い間当てはまりませんでした。最近では、NOT EXISTS
はわずかに高速です。
Accessでの最大の影響は、JOIN
メソッドが結合を完了する前に結合を完了し、結合されたセットをメモリに構築することです。 NOT EXISTS
を使用して行をチェックしますが、列にスペースを割り当てません。さらに、行が見つかると検索を停止します。 Accessではパフォーマンスが多少異なりますが、一般的な経験則では、NOT EXISTS
は少し高速になる傾向があります。関係する要素が多いので、「ベストプラクティス」とは言いたくないでしょう。
一般に、エンジンは基本的に以下に基づいて実行プランを作成します。
(4)の場合:
「存在しない」プランは、テーブルBのシークベースのプランを推奨します。これは、テーブルAが小さく、テーブルBが大きい(およびインデックスがBに存在する)場合に適しています。
「アンチジョイン」プランは、テーブルAが非常に大きいか、テーブルBが非常に小さいか、Bのインデックスがなく、大きな結果セットを返す場合に適しています。
ただし、これは重み付けされた入力のような単なる「励まし」です。強い(1)、(2)、(3)は、(4)の根拠のない選択をします。
(@MaxVernonの回答で対処されている*のために異なる列を返す例の影響を無視します。).