web-dev-qa-db-ja.com

OUTER JOIN内にネストされたINNER JOINの構文とクエリ結果

TLDR; 2つの実行プランを見ると、どちらが良いのか簡単な答えはありますか?意図的にインデックスを作成しなかったので、何が起こっているのかを簡単に確認できます。

私の前の質問 のフォローアップで、さまざまな結合スタイル(つまり、ネストされたものと従来のもの)の間のクエリパフォーマンスの違いが見つかりましたが、ネストされた構文もクエリの動作を変更することに気付きました。次の2つのクエリについて考えます。

SELECT  a.*, m.*, n.*
FROM    dbo.Autos a
LEFT JOIN dbo.Models m
  JOIN dbo.Manufacturers n  -- <-- Nested INNER JOIN
  ON n.ManufacturerID = m.ManufacturerID
ON m.ModelID = a.ModelID

enter image description here

これは、ModelIDが[〜#〜] not [〜#〜]の自動行を含めるために、製造元を結合する必要はありません。モデルテーブル内。

enter image description here

従来の構文を使用して、結合をManufacturesから外部結合に変更する必要があります。ただし、これによりクエリプランが変更されます。

SELECT a.*, m.*, n.*
FROM dbo.Autos a
LEFT JOIN dbo.Models m
ON m.ModelID = a.ModelID
LEFT JOIN dbo.Manufacturers n -- <-- Now LEFT OUTER JOIN
ON n.ManufacturerID = m.ManufacturerID

enter image description here

10
JeffInCO

2つの実行プランを見ると、どちらが良いのか簡単な答えはありますか?意図的にインデックスを作成しなかったので、何が起こっているのかを簡単に確認できます。

2番目の計画は推定コストが低いため、その限られた意味では「より良い」と言えます。

データセットは非常に小さいため、オプティマイザは代替手段を探すのにあまり時間をかけませんでした。クエリの最初の形式は、ハッシュ結合とテーブルスプールを使用して早い段階でプランを見つけることです。その計画の推定コストは非常に低いので、オプティマイザはより良いものを探す気になりません。

クエリの2番目の形式は、検索プロセスの早い段階でネストされたループの外部結合のみを使用してプランを見つけることがあり、再びオプティマイザがそのプランで十分であると判断します。たまたま、この計画はより安いと推定されています。

とはいえ(質問のコメントで述べたように)、2つのクエリは意味的に同一ではありません。データベースの将来のすべての状態で結果が常に同じであることを保証できる場合、これは重要ではありませんが、オプティマイザはその仮定を行うことができません。すべての状況で、SQLによって指定された同じ結果を生成するために保証である計画を生成するだけです。

ネストされた構文はクエリの動作も変更することを理解しました。

「ネストされた構文」は、ANSI結合構文仕様全体の1つの側面にすぎません。より複雑な結合パターンの完全な論理仕様を有効にするために、仕様では(オプションの)括弧とFROM句のサブクエリを使用できます。

クエリは次のように記述できます同じANSI構文を使用括弧を使用:

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Autos AS A
LEFT JOIN
(
    dbo.Manufacturers AS N
    JOIN dbo.Models AS M
        ON M.ManufacturerID = N.ManufacturerID
) ON M.ModelID = A.ModelID;

このフォームは、論理的な要件がAutosから内部結合ManufacturersからModelsの-​​resultへの左結合であることを明確に示しています。オプションの括弧を省略すると、「ネストされた」という形式になります。

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Autos AS A
LEFT JOIN dbo.Manufacturers AS N
JOIN dbo.Models AS M
    ON M.ManufacturerID = N.ManufacturerID
    ON M.ModelID = A.ModelID;

これは別の構文ではありません-オプションの括弧を省略し、少し再フォーマットするだけです。

Martinが述べたように、この場合、内部結合とそれに続く右外部結合を使用して論理要件を表現することもできます。

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Manufacturers AS N
JOIN dbo.Models AS M
    ON M.ManufacturerID = N.ManufacturerID
RIGHT JOIN dbo.Autos AS A
    ON A.ModelID = M.ModelID;

上記の3つのクエリ形式はすべて、同じANSI結合構文を使用します。また、3つすべてが、提供されたデータセットを使用して同じ物理実行プランを生成します。

Common execution plan

前の質問への回答で述べたように、まったく同じ論理要件を表すクエリが常に同じ実行プランを生成するとは限りません。どの論理クエリフォームを使用するかは、主にスタイルの問題です。一般的に、特定のスタイルと「より良い」クエリプランの間には相関関係はありません。新しいクエリが元のクエリと完全に論理的に同一でない場合は、通常、クエリを書き直して特定のプランを取得しないことをお勧めします。

SQL標準ではFROM句のサブクエリも許可されているため、同じクエリ仕様を作成する別の方法は次のとおりです。

SELECT * 
FROM dbo.Autos AS A
LEFT JOIN
(
    SELECT
        N.ManufacturerID,
        ManufacturerName = N.Name,
        M.ModelID,
        ModelName = M.Name
    FROM dbo.Manufacturers AS N
    JOIN dbo.Models AS M
        ON M.ManufacturerID = N.ManufacturerID
) AS R1
    ON R1.ModelID = A.ModelID;

従来の構文を使用して、結合を「メーカーなどの外部結合に変更する必要があります。ただし、これによりクエリプランが変更されます。

これはおそらくクエリの意味を変更します。その場合、技術的に有効な代替手段ではありません(ただし、質問の ypercubecomment を参照してください)。

ANSI結合構文の(オプションの)括弧は、このようなより複雑な結合要件のために正確に存在するので、必要な場所でそれらを使用することをためらわないでください。

12
Paul White 9