パフォーマンスに関して、左結合と内部結合に違いはありますか? SQL Server 2012 を使用しています。
2 に答える
LEFT [OUTER] JOIN
がよりも優れたオプションであるケースが少なくとも 1 つあります[INNER] JOIN
。OUTER
の代わりに を使用して同じ結果を得ることについて話しINNER
ます。
例 ( AdventureWorks 2008 データベースを使用しています):
-- Some metadata infos
SELECT fk.is_not_trusted, fk.name
FROM sys.foreign_keys fk
WHERE fk.parent_object_id=object_id('Sales.SalesOrderDetail');
GO
CREATE VIEW View1
AS
SELECT h.OrderDate, d.SalesOrderDetailID, o.ModifiedDate
FROM Sales.SalesOrderDetail d
INNER JOIN Sales.SalesOrderHeader h ON d.SalesOrderID = h.SalesOrderID
INNER JOIN Sales.SpecialOfferProduct o ON d.SpecialOfferID=o.SpecialOfferID AND d.ProductID=o.ProductID;
GO
CREATE VIEW View2
AS
SELECT h.OrderDate, d.SalesOrderDetailID, o.ModifiedDate
FROM Sales.SalesOrderDetail d
INNER JOIN Sales.SalesOrderHeader h ON d.SalesOrderID = h.SalesOrderID
LEFT JOIN Sales.SpecialOfferProduct o ON d.SpecialOfferID=o.SpecialOfferID AND d.ProductID=o.ProductID;
GO
SELECT SalesOrderDetailID
FROM View1;
SELECT SalesOrderDetailID
FROM View2;
最初のクエリの結果:
is_not_trusted name
-------------- ---------------------------------------------------------------
0 FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID
0 FK_SalesOrderDetail_SpecialOfferProduct_SpecialOfferIDProductID
最後の 2 つのクエリの実行計画:
注 1 / ビュー 1:の実行計画SELECT SalesOrderDetailID FROM View1
を見ると、制約が信頼されており、列が 1 つしかないため、FK が削除されていることがわかります。ただし、句にこのテーブルの列が含まれておらず、FK 制約 (FK_SalesOrderDetail_SpecialOfferProduct_SpecialOfferIDProductID) が (また) 信頼されていてもFK_SalesOrderDetail_SalesOrderHeader_SalesOrderID
、サーバーは (理由によりINNER JOIN Sales.SpecialOfferProduct
) 3 番目のテーブル (SpecialOfferProduct) からデータを読み取ることを強制されます。SELECT/WHERE
これは、この最後の FK が複数列であるために発生します。
注 2 / ビュー 2:Scan
の読み取り ( / Seek
)を削除したい場合はどうすればよいSales.SpecialOfferProduct
ですか? この 2 番目の FK は複数列であり、そのような場合、SQL Server は FK を削除できません (以前の Conor Cunnigham のブログ記事を参照してください)。この場合、FK を除去するために をINNER JOIN Sales.SpecialOfferProduct
に置き換える必要があります。列と列のLEFT OUTER JOIN Sales.SpecialOfferProduct
両方があり、信頼できる FK 参照テーブルがあります。SpecialOfferID
ProductID
NOT NULL
SpecialOfferProduct
追加の行が保持されるために外部結合がより大きな結果セットを返す可能性があるという問題と同様に、もう1つのポイントは、INNER JOIN
可換で連想的であるため、オプティマイザーが実行プランを作成する際の可能性が広がることです。
したがって、次の例B
ではインデックスが付けられていますが、そうでA
はありません。
CREATE TABLE A(X INT, Filler CHAR(8000))
INSERT INTO A
SELECT TOP 10000 ROW_NUMBER() OVER (ORDER BY @@SPID), ''
FROM sys.all_columns
CREATE TABLE B(X INT PRIMARY KEY, Filler CHAR(8000))
INSERT INTO B
SELECT TOP 10000 ROW_NUMBER() OVER (ORDER BY @@SPID), ''
FROM sys.all_columns
SELECT *
FROM B INNER JOIN A ON A.X = B.X
SELECT *
FROM B LEFT JOIN A ON A.X = B.X
オプティマイザーはそれを認識してB INNER JOIN A
おり A INNER JOIN B
、同じであり、テーブルをシークするネストされたループを含むプランを作成しますB
。
この変換は外部結合には無効であり、ネストされたループは右外部結合ではなく左外部結合のみをサポートするため、別の結合タイプを使用する必要があります。
ただし、実用的な観点からは、正しいセマンティクスを提供する必要な結合タイプを選択する必要があります。