feO*_*O2x 3 t-sql sql-server exception
我正在 TSQL 中编写一个存储过程,它使用事务和游标。我想知道是否应该将CLOSEandDEALLOCATE放在 TRY 块中 - 这些语句会抛出异常吗?
我的代码结构如下:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
DECLARE myCursor CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY FOR
SELECT -- Select statement left out for brevity's sake
BEGIN TRY
OPEN myCursor
FETCH NEXT FROM myCursor -- INTO statement left out for brevity's sake
WHILE @@FETCHSTATUS = 0
BEGIN
-- Here I process each item in the cursor
-- and then fetch the items for the next loop run
FETCH NEXT FROM myCursor
END;
CLOSE myCursor;
DEALLOCATE myCursor;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
CLOSE myCursor;
DEALLOCATE myCursor;
ROLLBACK TRANSACTION;
THROW;
END CATCH
Run Code Online (Sandbox Code Playgroud)
我有以下具体问题:
CLOSE抛出DEALLOCATE异常吗?如果是,这意味着我需要另一个 TRY-CATCH 块来安全地关闭游标,否则,TRY 块中发生的实际错误可能会丢失。我在官方文档中找不到任何相关信息,但这个答案表明它不会这样做。ROLLBACK TRANSACTIONCATCH 块中的语句吗?抛出异常时事务是否自动回滚?CLOSEand语句吗?DEALLOCATE马丁·史密斯发表评论后更新:
根据这个SO答案,我可以声明一个游标变量,当该变量超出范围时,该变量会自动关闭并释放。我不能 100% 确定这是否真的发生:根据DEALLOCATE 文档,DEALLOCATE被调用,但在CLOSE 文档中,没有任何关于自动行为的说明。
附加问题:
在这种特定情况下可以
CLOSE抛出DEALLOCATE异常吗?
我不明白任何人在任何情况下都会抛出错误。他们只是释放资源。如果游标已关闭或释放(取决于预期的情况),则期望游标存在的下游操作当然可能会出错
我应该删除块
ROLLBACK TRANSACTION中的语句CATCH吗?抛出异常时事务是否自动回滚?
不,不要删除ROLLBACK. 无论如何,您都需要它,尽管这通常包含在IF (@@TRANCOUNT > 0).
如果XACT_ABORT是OFF(默认),那么交易可能仍然处于打开状态,因此您需要执行ROLLBACK. 如果XACT_ABORT是ON,则事务已经回滚,但事务也处于不可提交状态,此时您唯一能做的就是发出ROLLBACK. 因此,无论哪种方式,您都需要ROLLBACK.
有什么办法可以避免这段代码中出现重复的
CLOSEand语句吗?DEALLOCATE
不,不是真的。但是,您可以只使用该DEALLOCATE语句,因为它会首先关闭游标。(见下面的例子)
使用变量真的会自动关闭并释放游标吗?
不确定该变量,但我确实知道这是没有必要的。您所需要的只是让光标处于状态LOCAL(您已经在这样做),它将在批处理结束时自动释放。(见下面的例子)
如果它真的有效,为什么要调用
CLOSE只被引用一次的本地游标呢?
可能是因为大多数人不知道本地游标会在批处理结束时自动释放(我直到做了测试才可以回答这个问题),以及游标的默认范围(除非在数据库级别,我怀疑大多数人都会搞乱该设置,不知道它将如何影响内置存储过程)GLOBAL,所以他们确实需要重新分配它们。
以下测试显示了LOCAL和GLOBAL(大多数系统上的默认值)游标之间的差异,并且它DEALLOCATE本身就很好(假设您不需要再次重新运行游标查询):
DECLARE myLocalCursor CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY FOR
SELECT TOP (10) [object_id] FROM sys.objects;
SELECT 'DECLARE (local)' AS [Step], @@CURSOR_ROWS AS [Rows], *
FROM sys.dm_exec_cursors(@@SPID);
-- 1 row
GO
SELECT 'NEXT BATCH (local)' AS [Step], @@CURSOR_ROWS AS [Rows], *
FROM sys.dm_exec_cursors(@@SPID);
-- no rows
DECLARE myGlobalCursor CURSOR GLOBAL STATIC FORWARD_ONLY READ_ONLY FOR
SELECT TOP (10) [object_id] FROM sys.objects;
SELECT 'DECLARE (global)' AS [Step], @@CURSOR_ROWS AS [Rows], *
FROM sys.dm_exec_cursors(@@SPID);
-- 1 row
GO
SELECT 'NEXT BATCH (global)' AS [Step], @@CURSOR_ROWS AS [Rows], *
FROM sys.dm_exec_cursors(@@SPID);
-- 1 row (same cursor_id as previous SELECT)
DEALLOCATE myGlobalCursor;
SELECT 'DEALLOCATE (global)' AS [Step], @@CURSOR_ROWS AS [Rows], *
FROM sys.dm_exec_cursors(@@SPID);
-- no rows
GO
Run Code Online (Sandbox Code Playgroud)
和:
-- open cursor then deallocate (do not close first):
DECLARE CursorToDeallocate CURSOR GLOBAL STATIC FORWARD_ONLY READ_ONLY FOR
SELECT TOP (10) [object_id] FROM sys.objects;
SELECT 'DECLARE (global2)' AS [Step], @@CURSOR_ROWS AS [Rows], *
FROM sys.dm_exec_cursors(@@SPID);
-- 1 row
OPEN CursorToDeallocate;
SELECT 'OPEN (global2)' AS [Step], @@CURSOR_ROWS AS [Rows], *
FROM sys.dm_exec_cursors(@@SPID);
-- 1 row
DEALLOCATE CursorToDeallocate;
SELECT 'DEALLOCATE (global2)' AS [Step], @@CURSOR_ROWS AS [Rows], *
FROM sys.dm_exec_cursors(@@SPID);
-- no rows
GO
Run Code Online (Sandbox Code Playgroud)
从您的文档问题来看:
CLOSE到时候还需要打电话吗DEALLOCATE?是否有一个自动机制可以做到这一点?
一般来说,是的,你CLOSE首先保持光标分配,然后你就可以了DEALLOCATE。如果您DEALLOCATE先调用,则光标会自动关闭。通过仅关闭游标,它仍然被定义,并且可以通过再次执行它来重新运行OPEN(不需要重新声明它)。
关于自动CLOSE或DEALLOCATE:
LOCAL批处理结束时游标会自动关闭并释放
DEALLOCATE您能提供内部实际执行情况的信息吗?
据我所知,只需从(好吧,无论数据存储在何处)DEALLOCATE删除游标定义。sys.dm_exec_cursors当然,如果游标尚未关闭,则将其关闭(至少在某种意义上,没有游标意味着没有游标可以打开或关闭,或者维护任何锁或任何资源;公平地说,这仅仅描述了最终结果,而不是代码到达那里的路径,所以我不能从技术上说是否DEALLOCATE真的调用 CLOSE或只是重复由 处理的某些功能CLOSE,我也不确定这种区别是否对任何人以外的人来说很重要维护该代码)。
我做了一些测试,发现两者DEALLOCATE本身CLOSE具有相同的效果:表上基于“CURSOR”的锁被删除。我没有发现任何证据表明DEALLOCATE其本身留下了任何锁。我SCROLL_LOCKS在执行UPDATE ... WHERE CURRENT OF Cursor. 每个都在页面上锁定,FETCH在键上锁定,在表上锁定,所有锁定所有者类型均为。它本身复制了这 3 个锁作为所有者类型(嗯,页面锁和密钥锁类型存在细微差别)。在or后,仅保留事务锁。IUUIXCURSORUPDATETRANSACTIONDEALLOCATECLOSE