更新する行の数に応じて完全に異なるプランを使用するT-SQLクエリ


20

「TOP(X)」句を含むSQL UPDATEステートメントがあり、値を更新する行には約40億行あります。「TOP(10)」を使用すると、ほぼ瞬時に実行される1つの実行プランが得られますが、「TOP(50)」以上を使用すると、クエリは(少なくとも、待機中ではなく)終了しません。まったく異なる実行計画を使用します。小さいクエリは、インデックスシークとネストされたループ結合のペアを持つ非常に単純なプランを使用します。まったく同じクエリ(UPDATEステートメントのTOP句の行数が異なる)は、2つの異なるインデックスシークを含むプランを使用します、テーブルスプール、並列処理、その他多数の複雑さ。

「OPTION(USE PLAN ...)」を使用して、より小さいクエリによって生成された実行プランを強制的に使用しました。これを行うと、数秒で100,000行も更新できます。クエリプランが良好であることはわかっていますが、SQL Serverは少数の行のみが関係している場合にのみそのプランを選択します。更新でかなり多くの行数があると、最適ではないプランになります。

並列処理のせいかもしれないと思ったのでMAXDOP 1、クエリを設定しましたが、効果はありません-そのステップはなくなりましたが、選択/パフォーマンスの低下はありません。sp_updatestatsそれが原因ではないことを確認するために、今朝も走りました。

2つの実行計画を添付しました-短いものは速いものです。さらに、問題のクエリは次のとおりです(含まれているSELECTが小さい行カウントと大きい行カウントの両方の場合に高速であるように見えることに注意する価値があります)。

    update top (10000) FactSubscriberUsage3
               set AccountID = sma.CustomerID
    --select top 50 f.AccountID, sma.CustomerID
      from FactSubscriberUsage3 f
      join dimTime t
        on f.TimeID = t.TimeID
      join #mac sma
        on f.macid = sma.macid
       and t.TimeValue between sma.StartDate and sma.enddate 
     where f.AccountID = 0 --There's a filtered index on the table for this

クイックプランは次のとおりです。 クイック実行計画

そして、これは遅いものです: 遅い実行計画

クエリを設定している方法または実行計画に明らかな何かがありますか?それは、クエリエンジンが行っている悪い選択に役立つでしょうか?必要に応じて、関連するテーブル定義とそれらに定義されているインデックスも含めることができます。

データベースオブジェクトの統計のみのバージョンを要求した人たちへ: あなたがそれができるとは思いませんでしたが、それは完全に理にかなっています!他の人が自分で実行プランをテストできるように、統計のみのデータベース用のスクリプトを生成しようとしましたが、フィルター処理されたインデックスで統計/ヒストグラムを生成できます(スクリプトの構文エラーのようです)、私は運が悪い。フィルターを削除しようとしましたが、クエリプランは近くなりましたが、まったく同じではなく、グースチェイスで誰も送信したくありません。

更新といくつかのより完全な実行計画: まず、SQL SentryのPlan Explorerは信じられないほどのツールです。このサイトにある他のクエリプランの質問を見るまで、それが存在することすら知りませんでした。私のクエリがどのように実行されていたかについては、かなりの発言がありました。問題に取り組む方法がわかりませんが、彼らは問題が何であるかを明らかにしました。

10行、100行、および1000行の概要を以下に示します。1000行のクエリは、他のクエリとは一線を画しています。 ステートメントの要約

3番目のクエリの読み取り数はとんでもない数であることがわかります。したがって、明らかに完全に異なる処理を行っています。行数を含む推定実行計画を以下に示します。 1000行の推定実行計画: 1000行の推定実行計画

そして、ここに実行計画の実際の結果があります(ちなみに、「決して終了しない」ということは、「1時間で終了する」という意味です)。 1000行の実際の実行計画 1000行の実際の実行計画

最初に気づいたのは、dimTimeテーブルから予想どおりに60K行をプルする代わりに、実際にはBで16億行をプルすることです。私のクエリを見ると、dimTimeテーブルからどれだけ多くの行が引き戻されているのかわかりません。私が使用しているBETWEEN演算子は、ファクトテーブルの時間レコードに基づいて#macから正しいレコードをプルすることを保証するだけです。ただし、t.TimeValue(またはt.TimeID)を単一の値にフィルター処理するWHERE句に行を追加すると、数秒で100,000行を正常に更新できます。その結果、そして私が含めた実行計画で明らかにされたように、それは私のタイムテーブルが問題であることは明らかですが、この問題を回避し、精度を維持するために結合基準をどのように変更するかわかりません。何かご意見は?

参考までに、ここでは、100行更新の計画(行カウント付き)を示します。同じインデックスにヒットし、まだ大量の行があるが、同じ規模の問題には至っていないことがわかります。 行カウントを使用した100行の実行ここに画像の説明を入力してください


それはGOTTA Beの統計です。sp_updatestatisticsテーブルで走ったことがありますか?
JNK

@JNK:最初はそう思っていましたが、sp_updatestatsを変更なしで既に実行しています。私はそれをもう一度実行しましたが、クエリに関係するインデックスの統計を更新することは気にしませんでした。でもありがとう!
SqlRyan

2番目は、目に見える複雑さの一部を説明する狭い(行ごと)更新プランではなく、広い(インデックスごと)更新プランです。しかし、本当に唯一の違いは、結合順序でfrom #mac sma join f on f.macid = sma.macid join dimTime t on f.TimeID = t.TimeID and t.TimeValue between sma.StartDate and sma.enddatefrom #mac join t on t.TimeValue between sma.StartDate and sma.enddate join f on f.TimeID = t.TimeID and f.macid = sma.macid
マーティン・スミス

ここには何かがありません。高価なクエリプランでさえ、行を増分的に生成する必要があります。A TOP 50はまだ迅速に実行されるはずです。XMLプランをアップロードできますか?行数を調べる必要があります。TOP 50maxdop 1で、更新としてではなく、選択として実行し、計画を投稿できますか?(検索スペースを単純化/二分しようとしています)。
usr

@usrに参加するとt.TimeValue between sma.StartDate and sma.enddate、さらに多くの無駄な行が生成され、後でFactSubscriberに対する結合で除外され、最終結果にならない場合があります。
マーティンスミス

回答:


3

dimTimeのインデックスは変更されています。より迅速な計画では、_dtaインデックスを使用しています。まず、sys.indexesで仮想インデックスとしてマークされていないことを確認します。

@StartDateと@enddateの間にこのWHERE t.TimeValueのような開始/終了日を指定するだけでなく、#macテーブルを使用してフィルター処理することで、パラメーター化をバイパスできると考えています。その一時テーブルを取り除きます。


dtaプレフィックスインデックスは、名前をカスタマイズせずにDTA推奨事項に従って作成されたように見えます。仮想インデックスは、実際の実行プランには表示されません(また、ドキュメント化されていないコマンドがないと、推定インデックスにも表示されません)。2番目の提案がどのように機能するかわかりません。t.TimeValue between sma.StartDate and sma.enddate相関しているため、#tempテーブルの各行で変更できます。OPは何に置き換えますか?
マーティンスミス

結構、一時テーブルに十分な注意を払っていませんでした。
-william_a_dba

1
ただし、仮想インデックスは実際に実行計画を台無しにする可能性があります。仮の場合は、削除して再作成する必要があります。 blogs.technet.com/b/anurag_sharma/archive/2008/04/15/...
william_a_dba

DTAが完了前に終了/フリーズしない場合、仮想インデックスは残ります。DTAに問題がある場合は、手動でクリーンアップする必要があります。
-william_a_dba

1
@william_a_dba-ああ、あなたの今の意味がわかります(リンクを読んだ後)。クエリが継続的に再コンパイルされていれば、クエリは終了しませんでした。面白い!
マーティンスミス

1

プランの行数に関する詳細情報がない場合、予備的な推奨事項は、クエリで正しい結合順序を調整し、を使用して強制することOPTION (FORCE ORDER)です。最初の計画の結合順序を強制します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.