SQL Serverで依存外部結合をネストする必要がありますか?


9

これについてはさまざまな情報を聞きましたが、正規または専門家の意見を期待しています。

複数LEFT OUTER JOINのがあり、それぞれが最後のに依存している場合、それらをネストする方が良いですか?

不自然な例では、JOINすることMyParentに依存JOINするMyChildhttp://sqlfiddle.com/#!3/31022/5

SELECT
    {columns}
FROM
    MyGrandChild AS gc
LEFT OUTER JOIN
    MyChild AS c
        ON c.[Id] = gc.[ParentId]
LEFT OUTER JOIN
    MyParent AS p
        ON p.[id] = c.[ParentId]

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

http://sqlfiddle.com/#!3/31022/7と比較

SELECT
    {columns}
FROM
    MyGrandChild AS gc
LEFT OUTER JOIN
    (
    MyChild AS c            
    LEFT OUTER JOIN
        MyParent AS p
            ON p.[id] = c.[ParentId]
    )
    ON c.[Id] = gc.[ParentId]

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

上記のように、これらはSS2k8で異なるクエリプランを生成します


ネストされた結合を使用するのが好きです:michaeljswart.com/2012/09/when-i-use-nested-joinsただし、スタイルの問題かもしれません。
Michael J Swart、2014

@MichaelJSwartあなたのブログJOINは、扶養家族が次の場合にのみ議論するように見えますINNER JOIN
Matthew

1
「より良い」をどのように定義しますか?個人的に、私は最初の方がはるかに読みやすいと思います-私の心は関係をリバースエンジニアリングしようとすることに飛び跳ねません。持ってON ... ON行(括弧かどうか)に二度は非常に混乱しています。
アーロンバートランド

4
2つの方法の間にパフォーマンスの違いが見つからない場合、次の質問は自分自身に尋ねます。今夜バスに当たったか、宝くじに当選した場合、明日コードを引き継ぐ人が最も簡単に理解し、維持できるバージョンはどれですか。 ?
アーロンバートランド

1
use plan最初のではなく、その逆に2つ目のクエリプランを移植する際のヒントは動作します。
マーティン・スミス

回答:


3

これは絶対的な正解ではありませんが、SQL Fiddleに示されているネストされたループのクエリプランでは、USE PLANヒントを使用してクエリ2からクエリ1にプランを適用できたが、逆の操作を試みると失敗しました。

USE PLANヒントにクエリに対して正当であると確認できなかったプランが含まれているため、クエリプロセッサはクエリプランを作成できませんでした。USE PLANヒントを削除または置き換えます。プランの強制が成功する可能性を最大限に高めるには、USE PLANヒントで提供されるプランが、同じクエリに対してSQL Serverによって自動的に生成されるものであることを確認してください。

オプティマイザ変換ルール ReorderLOJN無効にすると、以前成功した計画のヒントも成功しなくなります。

大量のデータを実験してみると、SQL Serverは確か(A LOJ B) LOJ CA LOJ (B LOJ C)自然に変換できることを示していますが、その逆が真実であるという証拠はありませんでした。

最初のクエリのパフォーマンスが2番目のクエリよりも優れている非常に不自然なケースは、

DROP TABLE  MyGrandChild , MyChild,  MyParent

CREATE TABLE MyParent
(Id int)

CREATE TABLE MyChild
(Id int PRIMARY KEY
,ParentId int,
Filler char(8000) NULL)

CREATE TABLE MyGrandChild
(Id int
,ParentId int)

INSERT INTO MyChild
                      (Id, ParentId)
SELECT TOP (100000) ROW_NUMBER() OVER (ORDER BY @@SPID),
                     ROW_NUMBER() OVER (ORDER BY @@SPID)    
FROM master..spt_values  v1, master..spt_values                  

INSERT INTO MyGrandChild
                      (Id, ParentId)
OUTPUT INSERTED.Id INTO MyParent
SELECT TOP (3000) Id, Id AS ParentId
FROM MyChild
ORDER BY Id

SET STATISTICS IO ON;
SET STATISTICS TIME ON;

SELECT gc.Id       AS gcId,
       gc.ParentId AS gcpId,
       c.Id        AS cId,
       c.ParentId  AS cpId,
       p.Id        AS pId
FROM   MyGrandChild AS gc
       LEFT OUTER JOIN MyChild AS c
         ON c.[Id] = gc.[ParentId]
       LEFT OUTER JOIN MyParent AS p
         ON p.[Id] = c.[ParentId]

SELECT gc.Id       AS gcId,
       gc.ParentId AS gcpId,
       c.Id        AS cId,
       c.ParentId  AS cpId,
       p.Id        AS pId
FROM   MyGrandChild AS gc
       LEFT OUTER JOIN( MyChild AS c
                        LEFT OUTER JOIN MyParent AS p
                          ON p.[Id] = c.[ParentId])
         ON c.[Id] = gc.[ParentId] 

それは計画を与える

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

私にとって、クエリ1の経過時間は108ミリ秒でしたが、クエリ2の経過時間は1,163ミリ秒でした。

クエリ1

Table 'Worktable'. Scan count 0, logical reads 0 
Table 'MyChild'. Scan count 0, logical reads 9196
Table 'MyGrandChild'. Scan count 1, logical reads 7
Table 'MyParent'. Scan count 1, logical reads 5

クエリ2

Table 'MyParent'. Scan count 1, logical reads 15000
Table 'MyChild'. Scan count 0, logical reads 9000 
Table 'MyGrandChild'. Scan count 1, logical reads 7

したがって、最初の(「ネストされていない」)構文は、より多くの潜在的な結合順序を検討できるため、潜在的に有益であると暫定的に想定されているかもしれませんが、これを一般的なルールとして十分に信頼できる十分なテストを行っていません。

クエリ2のパフォーマンスが向上するカウンターの例を考え出すことは完全に可能です。両方を試して実行プランを確認してください。


-1

「ネストされた結合」と呼ばれるそのようなJOINタイプはありません。これは、読みやすさのためにJOINを記述する別のバリ​​エーションです。目的を理解するためだけに、それらを「サブクエリ」と見なすことができます。

あなたがコードの可読性についてもっと心配しているなら、私の見解は、それが彼らが授与できるものは個人の選択です。

また、クエリのパフォーマンスに懸念があり、「Force JOIN ORDER」ヒントがクエリで使用されていない場合、クエリが「ネストされた結合」または「すべての外部結合」で記述されているかどうかは関係ありません。SQLサーバーは、2つのテーブルを結合するコストや結果に基づいて注文を出します。SQL Serverは、一度に2つのデータセット間の結合のみを行います。

実際、SQLサーバーが2番目の部分を実行することを決定した場合、「ネストされた結合」の2番目の方法を想像してください。「MyChild AS c LEFT OUTER JOIN MyParent AS p ON p。[id] = c。[ParentId]」とこれらのテーブルNEXT LEFT JOINで破棄される行があるようにします。その場合、SQLサーバーはこれら2つのOUTER JOINを実行し、その結果を次のJOINに渡すために不要なリソースを費やしています。

ここで同様の質問をして適切に回答することもできます。 「ネストされた結合」構文について


1
では、FORCE JOIN ORDERヒントを使わずに異なるクエリプランを作成するのはなぜですか。
マシュー14

そのヒントがないと、JOIN順序を保証できません。また、あなたがそれを証明する別の実行プランを見ているように。たとえば、最初の方法では、「すべての外部結合を使用する」SQLサーバーがこの2つのいずれかを実行している可能性があります。最初に「MyChild + MyGrandChild」、次に「MyParent」に参加します。または、最初に「MyChild + MyParent」、次に「MyGrandChild」に参加します。
Anup Shah 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.