SqlCommandの一時テーブルとパラメーターの落とし穴

 


.NETのSqlCommandでローカル一時テーブルを作成する際、SQLにSqlParameterを含めると、直後のSqlCommandからその一時テーブルを参照できず、「オブジェクト名 '#temp' が無効です」というエラーが発生することがあります。


using SqlCommand comm1 = conn.CreateCommand();
comm1.CommandText = """
CREATE TABLE #temp (col INT NOT NULL);
INSERT INTO #temp (col) VALUES (@param);
""";

SqlParameter param = comm1.CreateParameter();
param.ParameterName = "@param";
param.Value = 1;
comm1.Parameters.Add(param);

await comm1.ExecuteNonQueryAsync();

using SqlCommand comm2 = conn.CreateCommand();
comm2.CommandText = "SELECT col FROM #temp;";

// Microsoft.Data.SqlClient.SqlException:
// オブジェクト名 '#temp' が無効です。
var value = await comm2.ExecuteScalarAsync();


原因は、パラメーター化されたSQLがSQL Server側でsp_executesqlとして実行されるためです。sp_executesql内で作成されたローカル一時テーブルは、その実行スコープ内のものとして扱われ、スコープ終了時に破棄されます。そのため、次のSqlCommandからは参照できません。

対策としては、まずパラメーターを使わずにローカル一時テーブルの作成のみを実行し、その後パラメーター化されたSQLでデータを操作します。あるいは、作成から利用までをすべて1つのSqlCommand内で完結させる方法もあります。


ローカル一時テーブルを複数のSqlCommandで利用する場合は、SQL Server側でどのスコープで実行されるかにも注意が必要です。これを意識することで、一時テーブルのスコープを意識して安全に実装できます。


Configuring parameters - ADO.NET Provider for SQL Server | Microsoft Learn


HM

インプラス株式会社

このブログの人気の投稿

googleドライブの同期は、フォルダ選択ができました。

技術メモ「503 Service Unavailable」

なかなか消えない亀アイコン