CURSOR の宣言時に、LOCAL を指定しないと既定の動作では GLOBAL な CURSOR になります。
DECLARE CURSOR (Transact-SQL)
GLOBAL な CURSOR は CLOSE しなかった場合、接続が閉じられる迄使用可能です。
Transact-SQL カーソルの名前のスコープ
で、System.Data.Sql.SqlConnection SqlConnection クラス (System.Data.SqlClient) でコネクションプーリングを使用した場合は、GLOBAL な CURSOR が解放されるか試してみました。
ストアド (GLOBAL な CURSOR を DEALLOCATE しない)
CREATE PROCEDURE [GlobalCursorTest] AS BEGIN DECLARE SampleCrsr CURSOR GLOBAL FOR SELECT Name FROM Table_1 OPEN SampleCrsr FETCH NEXT FROM SampleCrsr WHILE (@@FETCH_STATUS <> -1) BEGIN FETCH NEXT FROM SampleCrsr END CLOSE SampleCrsr; -- わざと CURSOR を解放しない -- DEALLOCATE SampleCrsr; END GOusing System; using System.Data; using System.Data.SqlClient; class Program { const String ConnStr = @"Data Source=<serverName>;Initial Catalog=<databaseName>;Integrated Security=True;Max Pool Size=1"; static void Main(string[] args) { ExecGlobal(); ExecGlobal(); Console.ReadKey(); } static void ExecGlobal() { ExecCore("[GlobalCursorTest]"); } static void ExecCore(String procedureName) { try { using (var conn = new SqlConnection(ConnStr)) using (var cmd = new SqlCommand(procedureName, conn)) { conn.Open(); cmd.CommandType = CommandType.StoredProcedure; cmd.ExecuteNonQuery(); // コメントアウトを外すと例外発生 // cmd.ExecuteNonQuery(); } } catch (SqlException ex) { Console.WriteLine(ex.Message); } } }
ストアドでは、GLOBAL な CURSOR を解放せずに、C# 側から呼び出しています。
C# 側から、同一接続で2回続けて呼び出すと例外が発生します。しかし、別の接続を用いて続けて呼び出しても例外が発生しません。
ってことは、コネクションプーリングを使用しても GLOBAL な CURSOR は解放されている様です。
しかし、GLOBAL な CURSOR を使わなければならない場合以外では、LOCAL を指定してる方が良さそうです。
LOCAL な CURSOR は変数のスコープ外になると、解放される様です。
ローカル カーソルは、ストアド プロシージャやトリガで実装されたカーソルを保護するために重要です。グローバル カーソルは、カーソルが宣言されているストアド プロシージャまたはトリガの外部から参照できます。したがって、グローバル カーソルは、ストアド プロシージャやトリガ外部のステートメントで誤って変更される可能性があります。ローカル カーソルは、cursor 出力パラメータとして呼び出し側に意図的に渡されない限り、ストアド プロシージャ外部からは参照できないので、グローバル カーソルより高いセキュリティ保護を実現できます 〜(中略) GLOBAL カーソルは、明示的に割り当てを解除されるか、接続が閉じられるまで使用できます。LOCAL カーソルは、LOCAL カーソルが作成されたストアド プロシージャ、トリガ、またはバッチが終了するときに、暗黙的に割り当てを解除されます。ただし、カーソルがパラメータとして渡された場合を除きます。また、LOCAL カーソルは、プロシージャを呼び出したコード内のカーソルを参照するパラメータや変数がスコープ外になったときに、暗黙的に割り当てを解除されます。
上記のストアドも、CURSOR 宣言時に GLOBAL -> LOCAL に変更すると、C# 側で同一接続から続けて呼び出しても例外が発生しません。
LOCAL は、SQL SERVER 2005 から使えるのかな?確か。