SQL Server ではキャッシュされているクエリの情報を確認する際に、sys.dm_exec_query_stats を使用することが多いかと思います。
この DMV にどのようにクエリの情報がキャッシュされているかの基本について少しまとめてみたいと思います。
今回は以下のクエリのキャッシュについて確認をしてみたいと思います。
INSERT INTO Table_1 VALUES(NEWID()) INSERT INTO Table_1 VALUES(NEWID()), (NEWID()) SELECT * FROM Table_1 UPDATE Table_1 SET Col1 = NEWID() SELECT * FROM Table_1 OPTION(RECOMPILE) GO 10 SELECT * FROM Table_1 OPTION(RECOMPILE) GO 10
このクエリですが、
- 2 個のバッチ
- 6 個のステートメント
で構成がされていることになります。
概略を書くと以下のように分類されます。
SQL Server では処理の大きな枠としてバッチがあり、バッチは複数のステートメントで構成することができます。
この考えは SQL Server Profiler 等でクエリの情報を取得する際にも重要となります。
SQL Server Profiler では、
の情報を取得することができ、これらはバッチ / ステートメントのどちらの情報を取得した以下によって使い分けます。
全体の処理時間を見たい時はバッチ、各ステートメント単位の処理時間を見たい時はステートメントで取得するというのがわかりやすいイメージかもしれないですね。
それでは本題のクエリのキャッシュについて。
先ほどのクエリがどのようにキャッシュされているかを sys.dm_exec_query_stats から確認をしてみます。
sys.dm_exec_query_stats はバッチ単位ではなく、ステートメント単位で情報が格納されている DMV となります。
キャッシュされたクエリ プランの集計パフォーマンス統計を SQL Server に返します。 このビューには、キャッシュされたプラン内のクエリ ステートメントごとに 1 行が含まれており、その行の有効期間はプラン自体に関連付けられています。 つまり、プランがキャッシュから削除されると、対応する行もこのビューから削除されます。
sys.dm_exec_sql_text と CROSS APPLY をして、SQL のテキスト情報を取得することができますが、この時に取得できるテキストの情報はバッチ全体の情報となります。
対象のバッチのテキストの中で、自分が対象となるステートメントがどこになるかを「statement_start_offset」「statement_end_offset」で示しており、バッチ全体のテキストからこのオフセットの情報を使用して文字を分割することで対象のステートメントの情報を取得することができます。
また、SQL Server 2005 以降は、ステートメントレベルでのリコンパイルをサポートしているため、バッチ内にリコンパイルがあった場合には該当のステートメントは実行のたびにリコンパイルをさせることができます。
そのため、1 個めのバッチを 10 回実行した結果のキャッシュで OPTION RECOMPILE を指定したステートメントについては 1 回の実行となっていることが確認できます。
2 個めのバッチについては、バッチ = 1 ステートメントとなっており、バッチ全体がリコンパイルされることになりますので、こちらについてはクエリはキャッシュされていないことが確認できます。
これは、ストアドプロシージャでクエリを実行した場合も同じになります。
テキストとしてはストアドプロシージャ全体が取得できていますが、オフセットを使用したステートメントとなると、ストアドプロシージャ内の細かなステートメントレベルでの情報取得となっています。
リコンパイルについても同様の動作となっていますね。