実行プランの新しいプロパティ「ActualRowsRead」(SQL Server ManagementStudioでは「NumberofRowsRead」として表示されます)は、パフォーマンスチューナーへの歓迎すべき追加でした。これは、新しい超能力を持っているようなもので、Seek演算子内のSeekPredicateとResidualPredicateの重要性を伝えることができます。クエリを実行するのに非常に重要な場合があるため、これが大好きです。
私がAdventureWorks2012に対して実行している2つのクエリを見てみましょう。それらは非常に単純です。1つはジョンSと呼ばれる人々をリストし、もう1つはJスミスと呼ばれる人々をリストします。すべての優れた電話帳と同様に、LastName、FirstNameにインデックスがあります。
select FirstName, LastName from Person.Person where LastName like 'S%' and FirstName = 'John'; select FirstName, LastName from Person.Person where LastName = 'Smith' and FirstName like 'J%';
ご参考までに、最初の行から2行、2番目の行から14行を取得します。私は実際には結果にはそれほど興味がありません。実行計画に興味があります。
何が起こっているのか見てみましょう。 SQL Sentry Plan Explorerの古いコピーを開き、プランを並べて開きました。ちなみに、私は両方のクエリを一緒に実行したので、両方のプランは同じ.sqlplanファイルにありました。しかし、同じファイルをPEで2回開いて、タブグループに並べて配置することもできました。
素晴らしい。彼らは同じように見えます!左側のSeekは、14行ではなく2行を生成していることがわかります。明らかに、これがより適切なクエリです。
しかし、ウィンドウが大きいほど、より多くの情報が表示され、2つのクエリを同じバッチで実行できたのは幸運でした。
2行ではなく14行を生成した2番目のクエリは、コストの80%以上を占めると推定されたことがわかります。クエリを個別に実行すると、それぞれが100%表示されます。
それでは、PlanExplorerの最新リリースと比較してみましょう。
すぐに私に飛び出すのは警告です。もう少し詳しく見てみましょう。
警告には、「操作によりIOが残っています。実際に読み取られた行数は2,130でしたが、返された行数は2でした。」案の定、さらに上には、「実際の行の読み取り」が2,130と表示され、実際の行が2と表示されます。
うわあ!それらの行を見つけるために、2,130を調べなければなりませんでしたか?
ほら、Seekが実行される方法は、Seek述語について考えることから始めることです。これは、インデックスをうまく活用し、実際に操作をシークにするものです。シーク述語がない場合、操作はスキャンになります。ここで、このシーク述語が最大で1行であることが保証されている場合(一意のインデックスに等式演算子がある場合など)、シングルトンシークがあります。それ以外の場合は、範囲スキャンがあり、この範囲にはプレフィックス、開始、および終了を含めることができます(ただし、必ずしも開始と終了の両方である必要はありません)。これにより、Seekに関心のあるテーブルの行が定義されます。
しかし、「興味がある」とは、必ずしも「戻ってきた」という意味ではありません。やるべきことがもっとあるかもしれないからです。その作業は、残りの述語としてよく知られている他の述語で説明されています。
これで、ResidualPredicateが実際にほとんどの作業を行っている可能性があります。それは確かにここにあります–それは2,130行からわずか2行に物事をフィルタリングしています。
範囲スキャンは、「JohnS」のインデックスから始まります。 「ジョンS」がいる場合、これはすべてを満たすことができる最初の行でなければならないことを私たちは知っています。 「イアンS」はできません。そのため、その時点でインデックスを検索して、範囲スキャンを開始できます。 Plan XMLを見ると、これを明示的に確認できます。
プレフィックスがないことに注意してください。これは、インデックス内の最初の列が等しい場合に適用されます。 StartRangeとEndRangeがあります。範囲の開始は「GreaterThanorEqual」(GE)ScanTypeで、値は「S、John」(画面外の列参照はLastName、FirstName)であり、範囲の終了は「Less Than」( LT)値T。スキャンがTに達すると、完了します。これ以上することはありません。これで、Seekは範囲スキャンを完了しました。この場合、2,130行が返されます。
実際には2,130行を返さないことを除いて、2,130行を読み取るだけです。 Barry SaiやKenSánchezなどの名前が読み取られますが、次のチェックを満たす名前(FirstNameがJohnであることを確認するResidualPredicate)のみが返されます。
IndexSeekオペレーターのプロパティのActualRowsReadエントリは、この値2,130を示しています。また、Plan Explorerの以前のリリースでは表示されていますが、警告は表示されません。それは比較的新しいことです。
2番目のクエリ(Jスミスを探す)の方がはるかに優れており、4倍以上安いと推定されたのには理由があります。
ここでは、LastNameが正確にわかっており(Smith)、範囲スキャンはFirstName(J%)にあります。
これがプレフィックスの出番です。
プレフィックスは等式演算子(=、ScanType =” EQ”)であり、LastNameはSmithでなければならないことがわかります。範囲の開始または終了についてはまだ検討していませんが、プレフィックスは、範囲がインデックスのLastNameがSmithである部分に含まれていることを示しています。これで、行>=Jおよび
ここにはまだ残差述語がありますが、これは「LIKE J%」が実際にテストされていることを確認するためだけのものです。 「LIKEJ%」は「> =Jおよび
SQLServer2012のServicePack3より前は、このプロパティはありませんでした。実際の行の読み取りと実際の行の違いを理解するには、トレースフラグ9130を使用する必要がありました。これら2つの計画は次のとおりです。そのTFがオンになっている場合:
Seek演算子が2130行すべてを返しているため、今回は警告がないことがわかります。この実際の行の読み取りをサポートするバージョンのSQLServerを使用している場合は、調査でトレースフラグ9130の使用を停止し、代わりにプランエクスプローラーで警告を確認する必要があると思います。しかし、何よりも、オペレーターがどのように作業を行っているかを理解してください。そうすれば、計画に満足しているかどうか、または行動を起こす必要があるかどうかを解釈できるようになります。
別の投稿では、実際の行の読み取りが実際の行よりも高くなることを希望する場合の状況を紹介します。
@rob_farley