ストアドプロシージャには、次のカーソルがあります。
DECLARE @cur CURSOR
SET @cur = CURSOR LOCAL FORWARD_ONLY READ_ONLY
FOR SELECT -- various columns
FROM @localTableVariable
...そして、それを開いて、繰り返して、閉じます。(はい、セット操作を行うのではなく、繰り返す理由があります。)
エラーが発生した場合、そのカーソルは閉じられますか/クリーンアップされますか?たとえば、のMSDNページCLOSE
にはそれについて何も表示されません。または、TRY
/を実装CATCH
してストアドプロシージャコードで閉じる必要がありますか?(そしてRAISEERROR
、エラーを許可して、エラーが通常のエラー処理に伝播できるようにします。)
答えは「はい、クリーンアップされます」でなければならないと思いますが...
カーソルがテーブル変数ではなく実際のテーブルにある場合、答えは同じでしょうか?
(これは、SET XACT_ABORT ON
関連する場合、プロシージャが持つ明示的なトランザクション内にあります。編集:そして、私の特定のユースケースではSET CURSOR_CLOSE_ON_COMMIT ON
、トランザクションがコミットまたはロールバックされるため、を使用して閉じることができるようです。ストアドプロシージャの終わりですが、一般的なケースについては興味があります。)
SubqueryCrunchからの非常に有用なコメントに基づいて、少なくともSQL Server 2012では、使用しているオプションを使用して、カーソルが(すべてのトランザクションを含まずに)クリーンアップされることを経験的に証明しました。
CREATE PROCEDURE tjtemp
AS
BEGIN
DECLARE @foo TABLE
(
id INT
)
DECLARE @id INT
INSERT INTO @foo (id)
VALUES (1), (2), (3), (4), (5), (6)
DECLARE cur CURSOR LOCAL FORWARD_ONLY READ_ONLY
FOR SELECT id FROM @foo
OPEN cur
SELECT 'inside' AS Label, * FROM sys.dm_exec_cursors(0) WHERE name = 'cur'
FETCH NEXT FROM cur INTO @id
WHILE @@fetch_status = 0
BEGIN
PRINT @id
IF @id = 4
RAISERROR(N'ack!', 18, 1);
FETCH NEXT FROM cur INTO @id
END
CLOSE cur
END
GO
SELECT 'before' AS Label, * FROM sys.dm_exec_cursors(0) WHERE name = 'cur'
GO
EXEC tjtemp
GO
SELECT 'after' AS Label, * FROM sys.dm_exec_cursors(0) WHERE name = 'cur'
GO
(これは、カーソル変数ではなく名前付きカーソルを使用しますが、以下を参照してください。)
結果:
メッセージ:
(影響を受ける行は0) (影響を受ける6行) (影響を受ける1行) 1 2 3 4 メッセージ50000、レベル18、状態1、手順tjtemp、行24 ack! (影響を受ける行は0)
カーソルは、プロシージャが開始される前には存在せず、プロシージャが開いているときに存在し、プロシージャがエラーで終了した後には存在しないことがわかります。
ドキュメントで何かを見つけたいのですが、これは「確かにそのように機能する必要がある」という直感をサポートします。
カーソル変数を使用すると、のname
列にdm_exec_cursors
変数の名前が付けられることがわかったので、変数についてもそれを証明できました。
DROP PROCEDURE tjtemp
GO
CREATE PROCEDURE tjtemp
AS
BEGIN
DECLARE @foo TABLE
(
id INT
)
DECLARE @id INT
DECLARE @cur CURSOR
INSERT INTO @foo (id)
VALUES (1), (2), (3), (4), (5), (6)
SET @cur = CURSOR LOCAL FORWARD_ONLY READ_ONLY
FOR SELECT id FROM @foo
OPEN @cur
SELECT 'inside' AS Label, * FROM sys.dm_exec_cursors(0) WHERE name = '@cur'
FETCH NEXT FROM @cur INTO @id
WHILE @@fetch_status = 0
BEGIN
PRINT @id
IF @id = 4
RAISERROR(N'ack!', 18, 1);
FETCH NEXT FROM @cur INTO @id
END
CLOSE @cur
END
GO
SELECT 'before' AS Label, * FROM sys.dm_exec_cursors(0) WHERE name = '@cur'
GO
EXEC tjtemp
GO
SELECT 'after' AS Label, * FROM sys.dm_exec_cursors(0) WHERE name = '@cur'
GO
(同じ結果です。)
しかし、念のためWHERE
に、select from dm_exec_cursors
(DBで他の誰も何もしていないとき)に句を付けずに実行しました。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加