都内で働くSEの技術的なひとりごと / Technical soliloquy of System Engineer working in Tokyo

都内でサラリーマンやってます。SQL Server を中心とした (2023年からは Azure も。) マイクロソフト系(たまに、OSS系などマイクロソフト以外の技術も...)の技術的なことについて書いています。日々の仕事の中で、気になったことを技術要素関係なく気まぐれに選んでいるので記事内容は開発言語、インフラ等ばらばらです。なお、当ブログで発信、発言は私個人のものであり、所属する組織、企業、団体等とは何のかかわりもございません。ブログの内容もきちんと検証して使用してください。英語の勉強のため、英語の

SQL Server の実行プランを少しだけ詳しく説明したみた

 SQL Server Management Studio の知っておいたほうが良い機能について挙げてみる - その3 -  で使用していた SQL を検証がしやすいよう下記のように変更したいと思います。( NESTED LOOPS をわざと発生するようにしています。)

USE AdventureWorks2012
SET STATISTICS PROFILE ON SELECT * FROM [Production].[Product] a INNER JOIN [Production].[ProductInventory] b ON a.ProductID = b.ProductID OPTION(LOOP JOIN)

f:id:koogucc11:20130929225528j:plain

 まずは、実行プランの見方です。前回の記事で、実行プランは下から評価していくと記載していました。上からの評価が正しいです。(スイマセン。) 上図実行プランの詳細な見方を説明したいと思います。まず重要なのは、下記2つカラムです。

  • Rows
    StmtText を実行した時に処理結果として、算出された行数です。
  • Executes
    StmtText を実行した回数です。回数は上位の StmtText の結果により変化します。

f:id:koogucc11:20130929225752j:plain

 上図の 3 行目の StmtText は下記の通りです。PK_Product_ProductID に対してClusterd Index Scan を 1  回行っています。

|--Clustered Index Scan(
OBJECT:([AdventureWorks2012].[Production].[Product].[PK_Product_ProductID] AS [a]))

 上図の4行目は、3行目のスキャン操作で得た 個々の ProductID をキーに、504 回の Clustered Index Seek を行っています。

|--Clustered Index Seek(
OBJECT:([AdventureWorks2012].[Production].[ProductInventory].[PK_ProductInventory_ProductID_LocationID] AS [b]),
SEEK:([b].[ProductID]=[AdventureWorks2012].[Production].[Product].[ProductID] as [a].[ProductID]) ORDERED FORWARD)

 Nested Loop は、外部テーブル ( 上図の実行プランでは、『 Product 』) から一行づつ取り出し、内部テーブル (上図の実行プランでは、『 ProductInventory 』) の行から一致する行を探します。Nested Loop をC# で表現すると下記のような感じです。( なぜ、ロジックにするのか? なんとなく分かり易いと思ったからです.... )

ProductionProductCollection ppc = ....; //外部テーブル
ProductionProductInventoryCollection ppic = .....; //内部テーブル

//今回の例では、この foreach 文が504回ループします。
//Clustered Index Scanに該当します。
foreach ( var ppc_record in ppc ) {
//下記の処理が、Clustered Index Seekに該当します。
foreach ( var ppic_record in ppic ) {
if ( ppic_record.ProductID == ppc_record.ProductID ) {
//同一のProductID であればレコードを生成して返す。みたいな感じでしょうか。
}
} }

 Nested Loop は、上記のような考えです。HASH, MERGE も説明しようかと思いましたが、 土日仕事だったので....今日はここまでにします。実行プランを読み解けるようになれば、自然と高速な SQL が書けるようになります。( 思います、多分)