読者です 読者をやめる 読者になる 読者になる

SqlCommand の実行を SQL Server Profiler でトレースしてみた

ado.net sql-server

SqlCommand の ExecuteReader でクエリを実行する時にパラメータを渡すと sp_executesql を使って実行されます。パラメータを使わないと sp_executesql は使われないようです。

MSDN より引用します。

ADO.NET での side-by-side 実行

.NET Framework version 1.1 以降では、ExecuteReader は、コマンドにパラメーターが含まれている場合だけ、sp_executesql ストアド プロシージャのコンテキストでコマンドを実行します。

SqlCommand クラス (System.Data.SqlClient)

パフォーマンスを高めるために、ExecuteReader は Transact-SQL sp_executesql システム ストアド プロシージャを使用してコマンドを呼び出します。

前々からこのあたりを確認したかったので、SQL Server Profiler を使ってトレースしてみました。上記リンク先では触れられていない ExecuteNonQuery についても念のため確認してみました。

SQL Server Profiler でトレースを開始

SQL Server Profiler を立ち上げて、トレースするイベントを選択します。 sp_executesql が使われるときはRPC:Completedイベントが、そうでないときはSQL:BatchCompletedイベントが発生するのでそれぞれ選択します。 テンプレートは「Standard」のままです。

f:id:ichiroku11:20140408234433p:plain

ExecuteReader と ExecuteNonQuery を実行

SqlCommand の ExecuteReader と ExecuteNonQuery の実行をトレースしてみます。

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp {
    class Program {
        static void Main(string[] args) {
            var connectionString = new SqlConnectionStringBuilder {
                DataSource = ".",
                InitialCatalog = "ConsoleApp",
                IntegratedSecurity = true,
            }.ToString();

            using (var connection = new SqlConnection(connectionString)) {
                connection.Open();

                // パラメータを使わないExecuteReader
                using (var command = new SqlCommand("select 1;", connection)) {
                    using (var reader = command.ExecuteReader()) {
                        while (reader.Read()) {
                            Console.WriteLine(reader[0]);  // 1
                        }
                    }
                }

                // パラメータを使うExecuteReader => sp_executesql
                using (var command = new SqlCommand("select @p;", connection)) {
                    command.Parameters.AddWithValue("@p", 2);
                    using (var reader = command.ExecuteReader()) {
                        while (reader.Read()) {
                            Console.WriteLine(reader[0]);  // 2
                        }
                    }
                }

                // create table Test(Id int);でテーブルが用意してあるとして
                // パラメータを使わないExecuteNonQuery
                using (var command = new SqlCommand("insert into Test values(3);", connection)) {
                    command.ExecuteNonQuery();
                }

                // パラメータを使うExecuteNonQuery => sp_executesql
                using (var command = new SqlCommand("insert into Test values(@p);", connection)) {
                    command.Parameters.AddWithValue("@p", 4);
                    command.ExecuteNonQuery();
                }
            }
        }
    }
}

結果

f:id:ichiroku11:20140408234451p:plain

select 1;
go
exec sp_executesql N'select @p;',N'@p int',@p=2
go
insert into Test values(3);
go
exec sp_executesql N'insert into Test values(@p);',N'@p int',@p=4
go

パラメータがある場合は ExecuteReader と ExecuteNonQuery のどちらでも sp_executesql が使われることを確認できました。

その他の参考情報