5

SQL Server 2008 R2(バージョン10.50.2806.0)でこのクエリが高速になるのはなぜですか

    SELECT
        MAX(AtDate1),
        MIN(AtDate2)
    FROM
    (
        SELECT TOP 1000000000000
            at.Date1 AS AtDate1,
            at.Date2 AS AtDate2
        FROM
            dbo.tab1 a
        INNER JOIN
            dbo.tab2 at
        ON
            a.id = at.RootId
        AND CAST(GETDATE() AS DATE) BETWEEN at.Date1 AND at.Date2
        WHERE
            a.Number = 223889
    )B  

それから

    SELECT
        MAX(AtDate1),
        MIN(AtDate2)
    FROM
    (
        SELECT 
            at.Date1 AS AtDate1,
            at.Date2 AS AtDate2
        FROM
            dbo.tab1 a
        INNER JOIN
            dbo.tab2 at
        ON
            a.id = at.RootId
        AND CAST(GETDATE() AS DATE) BETWEEN at.Date1 AND at.Date2
        WHERE
            a.Number = 223889
    )B  

属性を持つ2番目のステートメントTOPは6倍高速です。

内部サブクエリのcount(*)は9280行です。

HINTを使用して、SQL Serverオプティマイザーが正しく機能することを宣言できますか? 実行計画

4

2 に答える 2

3

計画を投稿したようです。引き分けの運だけ。

実際のクエリは16テーブル結合です。

SELECT max(atDate1)  AS AtDate1,
       min(atDate2)  AS AtDate2,
       max(vtDate1)  AS vtDate1,
       min(vtDate2)  AS vtDate2,
       max(bgtDate1) AS bgtDate1,
       min(bgtDate2) AS bgtDate2,
       max(lftDate1) AS lftDate1,
       min(lftDate2) AS lftDate2,
       max(lgtDate1) AS lgtDate1,
       min(lgtDate2) AS lgtDate2,
       max(bltDate1) AS bltDate1,
       min(bltDate2) AS bltDate2
FROM   (SELECT TOP 100000 at.Date1  AS atDate1,
                          at.Date2  AS atDate2,
                          vt.Date1  AS vtDate1,
                          vt.Date2  AS vtDate2,
                          bgt.Date1 AS bgtDate1,
                          bgt.Date2 AS bgtDate2,
                          lft.Date1 AS lftDate1,
                          lft.Date2 AS lftDate2,
                          lgt.Date1 AS lgtDate1,
                          lgt.Date2 AS lgtDate2,
                          blt.Date1 AS bltDate1,
                          blt.Date2 AS bltDate2
        FROM   dbo.Tab1 a
               INNER JOIN dbo.Tab2 at
                 ON a.id = at.Tab1Id
                    AND cast(Getdate() AS DATE) BETWEEN at.Date1 AND at.Date2
               INNER JOIN dbo.Tab5 v
                 ON v.Tab1Id = a.Id
               INNER JOIN dbo.Tab16 g
                 ON g.Tab5Id = v.Id
               INNER JOIN dbo.Tab3 vt
                 ON v.id = vt.Tab5Id
                    AND cast(Getdate() AS DATE) BETWEEN vt.Date1 AND vt.Date2
               LEFT OUTER JOIN dbo.Tab4 vk
                 ON v.id = vk.Tab5Id
               LEFT OUTER JOIN dbo.VerkaufsTab3 vkt
                 ON vk.id = vkt.Tab4Id
               LEFT OUTER JOIN dbo.Plu p
                 ON p.Tab4Id = vk.Id
               LEFT OUTER JOIN dbo.Tab15 bg
                 ON bg.Tab5Id = v.Id
               LEFT OUTER JOIN dbo.Tab7 bgt
                 ON bgt.Tab15Id = bg.Id
                    AND cast(Getdate() AS DATE) BETWEEN bgt.Date1 AND bgt.Date2
               LEFT OUTER JOIN dbo.Tab11 b
                 ON b.Tab15Id = bg.Id
               LEFT OUTER JOIN dbo.Tab14 lf
                 ON lf.Id = b.Id
               LEFT OUTER JOIN dbo.Tab8 lft
                 ON lft.Tab14Id = lf.Id
                    AND cast(Getdate() AS DATE) BETWEEN lft.Date1 AND lft.Date2
               LEFT OUTER JOIN dbo.Tab13 lg
                 ON lg.Id = b.Id
               LEFT OUTER JOIN dbo.Tab9 lgt
                 ON lgt.Tab13Id = lg.Id
                    AND cast(Getdate() AS DATE) BETWEEN lgt.Date1 AND lgt.Date2
               LEFT OUTER JOIN dbo.Tab10 bl
                 ON bl.Tab11Id = b.Id
               LEFT OUTER JOIN dbo.Tab6 blt
                 ON blt.Tab10Id = bl.Id
                    AND cast(Getdate() AS DATE) BETWEEN blt.Date1 AND blt.Date2
        WHERE  a.Nummer = 223889) B

良い計画と悪い計画の両方で、実行計画は「ステートメント最適化の早期終了の理由」を「タイムアウト」として示しています。

2つのプランの参加順序はわずかに異なります。

インデックスシークで満たされないプランへの唯一の参加は、での参加Tab9です。これには63,926行あります。

実行プランに欠落しているインデックスの詳細は、次のインデックスを作成することを示唆しています。

CREATE NONCLUSTERED INDEX [miising_index]
ON [dbo].[Tab9] ([Date1],[Date2])
INCLUDE ([Tab13Id])

悪い計画の問題のある部分は、SQL SentryPlanExplorerではっきりと見ることができます。

悪い計画

SQL Serverは、の結合に入る前の結合から1.349174行が返されると見積もっていますTab9。したがって、ネストされたループの結合には、内部テーブルでスキャンを1.349174回実行する必要があるかのようにコストがかかります。

実際、2,600行がその結合にフィードされます。つまり、 Tab9(2,600 * 63,926 = 164,569,600行)の2,600回のフルスキャンが実行されます。

たまたま、良い計画では、結合に入る行の推定数は2.74319です。これはまだ3桁間違っていますが、見積もりがわずかに増加しているということは、SQLServerが代わりにハッシュ結合を優先することを意味します。ハッシュ結合は1回のパススルーを実行するだけですTab9

いい計画

まず、不足しているインデックスをに追加してみますTab9

また/代わりに、関連するすべてのテーブル(特に、などの日付述語を持つテーブル)の統計を更新してTab2 Tab3 Tab7 Tab8 Tab6、計画の左側にある推定行と実際の行の間の大きな不一致を修正する方法があるかどうかを確認してください。

また、クエリを小さな部分に分割し、これらを適切なインデックスを持つ一時テーブルに具体化すると役立つ場合があります。SQL Serverは、これらの部分的な結果の統計を使用して、計画の後半で結合をより適切に決定できます。

最後の手段としてのみ、クエリヒントを使用して、ハッシュ結合を使用してプランを強制することを検討します。それを行うためのオプションは、USE PLANすべての結合タイプと順序を含めて、必要なプランを正確に指示する場合のヒントか、を指定することによって選択できますLEFT OUTER HASH JOIN tab9 ...。この2番目のオプションには、プラン内のすべての参加注文を修正するという副作用もあります。どちらも、SQL Serverが大幅に制限されることを意味します。これは、データ分散の変更に合わせて計画を調整する機能です。

于 2013-03-17T19:16:37.903 に答える
1

テーブルのサイズと構造がわからず、実行プラン全体を確認できないと答えるのは難しいです。ただし、両方のプランの違いは、「トップn」クエリのハッシュ一致結合と、もう一方のネストされたループ結合です。ハッシュ一致は、サーバーがハッシュバケットを使用するために準備する必要があるため、非常にリソースを消費する結合です。ただし、大きなテーブルの場合ははるかに効果的ですが、ネストされたループでは、あるテーブルの各行を別のテーブルのすべての行と比較すると、そのような準備が必要ないため、小さなテーブルに最適です。私が思うに、サブクエリでTOP 1000000000000行を選択すると、オプティマイザにサブクエリが大量のデータを生成するというヒントが与えられるため、ハッシュ一致が使用されます。ただし、実際には出力が小さいため、ネストされたループの方がうまく機能します。

于 2013-03-15T17:37:28.363 に答える