sql >> データベース >  >> RDS >> Database

読み取る行の推定数

    誤解しないでください–SQLServerの実行プランに到着したActualRowsReadプロパティが大好きですしかし、SQL Server 2016 SP1では、2か月も経たないうちに(そして、その間にクリスマスがあったことを考えると、それ以降はあまり重要ではないと思います)、もう1つのエキサイティングな追加がありました– 読み取る行の推定数 (ああ、これは私が提出したConnectアイテムにいくらか当てはまります。どちらも、Connectアイテムを送信する価値があることを示しており、ConnectアイテムのトピックについてBrent Ozar(@brento)がホストする今月のT-SQL火曜日にこの投稿を適格にすることを示しています。 。

    少し要約してみましょう…SQLエンジンがテーブル内のデータにアクセスするとき、スキャン操作またはシーク操作のいずれかを使用します。そして、そのSeekが最大で1つの行にアクセスできるSeek Predicateを持っていない限り(列のセットで同等の一致を探しているため、一意であることがわかっている単一の列である可能性があります)、SeekはRangeScanであり、Seek Predicateによって満たされる行のサブセット全体で、Scanと同じように動作します。

    Seek述語(Seek操作のRangeScanの場合)またはテーブル内のすべての行(Scan操作の場合)が満たす行は、基本的に同じ方法で処理されます。左側の演算子からこれ以上行が要求されない場合、たとえば、どこかのTop演算子がすでに十分な行を取得している場合、またはマージ演算子に一致する行がない場合、両方が早期に終了する可能性があります。また、Scan / Seekオペレーターによって行が提供される前に、両方がResidual Predicate(「Predicate」プロパティとして表示)によってさらにフィルター処理される場合があります。 「行数」および「推定行数」プロパティは、オペレーターによって生成されると予想される行数を示しますが、シーク述語だけで行をフィルター処理する方法についての情報はありませんでした。 TableCardinalityを確認できましたが、これはスキャンオペレーターにとってのみ非常に役立ちました。スキャンオペレーターは、スキャンがテーブル全体を調べて必要な行を探す可能性がありました。シークにはまったく役に立ちませんでした。

    ここで実行しているクエリは、WideWorldImportersデータベースに対するものであり、次のとおりです。

    SELECT COUNT(*)
    FROM Sales.Orders
    WHERE SalespersonPersonID = 7
    AND YEAR(OrderDate) = 2013
    AND MONTH(OrderDate) = 4;

    さらに、私はプレイ中のインデックスを持っています:

    CREATE NONCLUSTERED INDEX rf_Orders_SalesPeople_OrderDate 
      ON Sales.Orders (SalespersonPersonID, OrderDate);

    このインデックスはカバーしており、クエリは回答を得るために他の列を必要としません。また、Seek PredicateをSalespersonPersonIDで使用できるように設計されており、データをより狭い範囲にすばやくフィルタリングできます。 OrderDateの関数は、これらの最後の2つの述語をSeek述語内で使用できないため、代わりにResidualPredicateに委任されることを意味します。より良いクエリは、OrderDate> ='20130401' AND OrderDate <'20130501'を使用してこれらの日付をフィルタリングしますが、ここではあまりにも一般的なシナリオを想像しています…

    ここで、クエリを実行すると、残差述語の影響を確認できます。プランエクスプローラーは、私が以前に書いたような便利な警告を出します。

    RangeScanが7,276行であり、Residual Predicateがこれを149にフィルタリングしていることが非常にはっきりとわかります。プランエクスプローラーは、ツールチップにこれに関する詳細情報を表示します:

    しかし、クエリを実行しないと、その情報を見ることができません。そこにはありません。推定計画のプロパティにはそれがありません:

    そして、私はあなたに思い出させる必要はないと確信しています–この情報はプランキャッシュにも存在しません。以下を使用してキャッシュからプランを取得しました:

    SELECT p.query_plan, t.text
    FROM sys.dm_exec_cached_plans c
    CROSS APPLY sys.dm_exec_query_plan(c.plan_handle) p
    CROSS APPLY sys.dm_exec_sql_text(c.plan_handle) t
    WHERE t.text LIKE '%YEAR%';

    私はそれを開いた、そして確かに、その7,276の価値の兆候はなかった。先ほど示した見積もりと同じように見えます。

    キャッシュから計画を取得することは、推定値が独自のものになる場所です。顧客データベースに対して潜在的に高価なクエリを実際に実行したくないというだけではありません。プランキャッシュのクエリは1つのことですが、実際を取得するためにクエリを実行することは非常に困難です。

    SQL 2016 SP1がインストールされていると、そのConnectアイテムのおかげで、推定プランとプランキャッシュに[読み取られる行の推定数]プロパティが表示されるようになりました。ここに示されているオペレーターのツールチップはキャッシュから取得されたものであり、推定プロパティが7,276を示していることと、残りの警告が簡単にわかります。

    これは、顧客ボックスで実行できることであり、キャッシュを調べて、読み取る行の推定数と行の推定数の比率が大きくない問題のある計画の状況を探します。潜在的に、誰かがキャッシュ内のすべての計画をチェックするプロセスを作成する可能性がありますが、それは私が行ったことではありません。

    鋭敏な読書は、この演算子から出てきた実際の行が149であり、推定された1382.56よりもはるかに小さかったことに気付くでしょう。しかし、あまりにも多くの行をチェックしなければならない残差述語を探しているとき、1,382.56:7,276の比率は依然として重要です。

    このクエリは実行しなくても効果がないことがわかったので、それを修正する方法は、残余述語が十分にSARG可能であることを確認することです。このクエリ…

    SELECT COUNT(*) 
    FROM Sales.Orders
    WHERE SalespersonPersonID = 7 
    AND OrderDate >= '20130401' 
    AND OrderDate <  '20130501';

    …同じ結果が得られ、残差述語はありません。この状況では、読み取られる推定行数の値は推定行数と同じであり、非効率性はなくなります。

    前述のように、この投稿は今月のT-SQL火曜日の一部です。最近許可された他の機能リクエストを確認してみませんか?


    1. SQLServerのバージョン間での単一トランザクションのデッドロックに続く

    2. Android SQLite:テスト目的で大きなテーブルを生成する方法は?

    3. EBS12.2でWebポートを変更する方法

    4. MySQLで同等のDATALENGTH()とは何ですか?