Sql server 2005 包含TRY-CATCH回滚模式的嵌套存储过程?
我对以下模式的副作用和潜在问题感兴趣:Sql server 2005 包含TRY-CATCH回滚模式的嵌套存储过程?,sql-server-2005,linq-to-sql,stored-procedures,transactions,Sql Server 2005,Linq To Sql,Stored Procedures,Transactions,我对以下模式的副作用和潜在问题感兴趣: CREATE PROCEDURE [Name] AS BEGIN BEGIN TRANSACTION BEGIN TRY [...Perform work, call nested procedures...] END TRY BEGIN CATCH ROLLBACK TRANSACTION RAISERROR [rethrow caught error using @Erro
CREATE PROCEDURE [Name]
AS
BEGIN
BEGIN TRANSACTION
BEGIN TRY
[...Perform work, call nested procedures...]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
END CATCH
END
据我所知,当与单个过程一起使用时,此模式是合理的-该过程要么会无错误地完成其所有语句,要么会回滚所有操作并报告错误
但是,当一个存储过程调用另一个存储过程来执行某些子工作单元时(理解为较小的过程有时会自己调用),我看到一个与回滚相关的问题—一条信息性消息(级别16)发出声明回滚事务请求没有相应的开始事务。。我假设这是因为子过程中的回滚总是回滚最外层的事务,而不仅仅是子过程中启动的事务
如果发生任何错误(并且错误作为SQL错误报告给客户机),我确实希望整个过程回滚并中止,但我不确定是否有来自外层的所有副作用试图回滚已经回滚的事务。在每个TRY-CATCH层执行回滚之前,是否检查@@tracount
最后是客户端(Linq2SQL),它有自己的事务层:
try
{
var context = new MyDataContext();
using (var transaction = new TransactionScope())
{
// Some Linq stuff
context.SubmitChanges();
context.MyStoredProcedure();
transactionComplete();
}
}
catch
{
// An error occured!
}
如果在MyStoredProcedure中调用的存储过程“MySubProcedure”引发错误,我是否可以确保以前在MyStoredProcedure中完成的所有操作都将回滚,所有由SubmitChanges执行的Linq操作都将回滚,并最终记录错误?或者我需要在我的模式中改变什么以确保整个操作是原子的,同时仍然允许单独使用子部分(即子过程应该仍然具有相同的原子保护)我不是Linq guy(Erland也不是),但他写了关于错误处理的绝对圣经。除了Linq可能增加您的问题的复杂性之外,您的所有其他问题都应该在这里回答:
(旧链接:)这是我们的模板(错误日志记录已删除)
这是为了处理
- 保罗·兰德尔的文章
- 错误266
- 触发器回滚
- 所有TXN begin和commit/ROLLBACK必须成对,以便
在进入和退出时相同@@TRANCOUNT
的不匹配会导致错误266,因为@@TRANCOUNT
递增BEGIN TRAN
@@tracount
递减COMMIT
@@tracount
将回滚
返回为零@@tracount
- 对于当前作用域,不能递减
。@@tracount
这就是你认为的“内部交易”
抑制由不匹配的将XACT\U ABORT设置为ON
@@tracount引起的错误266
还可以在dba.se上处理类似的问题- 这允许客户端TXN(如LINQ) 单个存储过程可以是分布式或XA事务的一部分,也可以只是在客户端代码中启动的一个存储过程(例如.net TransactionScope)
- 每个存储的进程必须符合相同的模板
- 因此,不要创建超过您需要的TXN
CREATE PROCEDURE [Name]
AS
SET XACT_ABORT, NOCOUNT ON
DECLARE @starttrancount int
BEGIN TRY
SELECT @starttrancount = @@TRANCOUNT
IF @starttrancount = 0
BEGIN TRANSACTION
[...Perform work, call nested procedures...]
IF @starttrancount = 0
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0 AND @starttrancount = 0
ROLLBACK TRANSACTION;
THROW;
--before SQL Server 2012 use
--RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
END CATCH
GO
创建过程[名称]
作为
设置XACT_中止,不计数为ON
声明@starttrancount int
开始尝试
选择@starttrancount=@@TRANCOUNT
如果@starttrancount=0
开始交易
[…执行工作,调用嵌套过程…]
如果@starttrancount=0
提交事务
结束尝试
开始捕捉
如果XACT_STATE()为0且@starttrancount=0
回滚事务;
投掷;
--在SQL Server 2012之前使用
--RAISERROR[使用@ErrorNumber、@ErrorMessage等重新显示捕获的错误]
端接
去
注意事项:
- 回滚检查实际上是冗余的,因为
。然而,它让我感觉更好,没有它看起来很奇怪,并且允许出现你不想穿的情况将XACT\u ABORT设置为ON
- 具有使用保存点的。我更喜欢原子数据库调用,不使用像他们文章那样的部分更新
DECLARE @ErrorMessage NVARCHAR(4000)
DECLARE @ErrorSeverity INT
DECLARE @ErrorState INT
DECLARE @ErrorLine INT
DECLARE @ErrorNumber INT
SELECT @ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE(),
@ErrorNumber = ERROR_NUMBER(),
@ErrorLine = ERROR_LINE()
RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorNumber, @ErrorLine)
--上面的@Amanda方法没有返回正确的错误号
DECLARE
@ErrorMessage nvarchar(4000),
@ErrorSeverity int,
@ErrorState int,
@ErrorLine int,
@ErrorNumber int
BEGIN TRY
SELECT 1/0; -- CATCH me
END TRY
BEGIN CATCH
DECLARE @err int = @@ERROR
PRINT @err -- 8134, divide by zero
PRINT ERROR_NUMBER() -- 8134
SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE(),
@ErrorNumber = ERROR_NUMBER(),
@ErrorLine = ERROR_LINE()
-- error number = 50000 :(
RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorNumber, @ErrorLine)
END CATCH
-- error number = 8134
SELECT 1/0
如果CATCH中不需要特殊的错误处理,除了rethrow和stored procs调用链不太长,则可能适合使用以下简单模板:
create procedure someNestedSP
as
SET XACT_ABORT ON
begin transaction
-- do some work or call some other similar SP
commit transaction
如果出现任何错误,它还将使用所有“嵌套”事务回滚根事务,但代码比@gbn的解决方案更短、更直接。仍然XACT_ABORT
处理了这里提到的大多数问题
事务嵌套可能会有额外的开销,但我想可能不会太高。如果您的子过程依赖于外部过程(即事务)中的某些内容,那么在我看来,在不应该存在耦合的地方似乎存在大量耦合。编辑:我想我读到了你的问题错误设置NOCOUNT ON似乎没有必要?你应该总是在imho上使用SET NOCOUNT。@Carl R:SET NOCOUNT ON在代码和触发器中非常重要:没有它,很多客户端代码都会中断。此外,还可以测量处理“xx行受影响消息”所需的时间。请看(我的问题)。我认为@ROWCOUNT没有随着它的启用而更新,但我错了。谢谢@Niraj:如果您决定将存储过程嵌套得更深,或者希望直接调用内部存储过程,该怎么办。你在做假设。如果内部@@TRANCOUNT变为零(例如,批处理中止触发器错误),您仍然可以得到错误266