sql-server-2005 - 包含 TRY CATCH ROLLBACK 模式的嵌套存储过程?

标签 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 @ErrorNumber, @ErrorMessage, etc]
    END CATCH
END

据我所知,这种模式在与单个过程一起使用时是合理的 - 该过程将完成其所有语句而不会出现错误,或者将回滚所有操作并报告错误。

但是,当一个存储过程调用另一个存储过程来执行某些子工作单元时(了解较小的过程有时会单独调用),我看到一个与回滚相关的问题 - 一条信息性消息(级别 16) 发出,说明ROLLBACK TRANSACTION 请求没有相应的 BEGIN TRANSACTION。。我认为这是因为子过程中的回滚总是回滚最外层的事务,而不仅仅是子过程中启动的事务。

如果发生任何错误(并且该错误作为 SQL 错误报告给客户端),我确实希望整个事情回滚并中止,我只是不确定来自外层试图执行的所有副作用回滚已经回滚的事务。也许在每个 TRY CATCH 层执行回滚之前检查 @@TRANCOUNT

最后是客户端 (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会被回滚,最后那个错误会被记录吗?或者我需要改变我的模式以确保整个操作是原子的,同时仍然允许单独使用子部分(即子过程仍应具有相同的原子保护)

最佳答案

这是我们的模板(已删除错误日志记录)

这旨在处理

说明:

  • 所有 TXN 开始和提交/回滚必须配对,以便 @@TRANCOUNT 在进入和退出时相同

  • @@TRANCOUNT 不匹配会导致错误 266,因为

    • BEGIN TRAN 递增 @@TRANCOUNT

    • COMMIT 递减 @@TRANCOUNT

    • ROLLBACK@@TRANCOUNT 返回零

  • 您无法减少当前范围的@@TRANCOUNT
    这就是您所认为的“内部交易”

  • SET XACT_ABORT ON 抑制由不匹配的 @@TRANCOUNT 导致的错误 266
    并且还处理类似这样的问题 "SQL Server Transaction Timeout"在 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

注释:

  • 由于SET XACT_ABORT ON,回滚检查实际上是多余的。然而,它让我感觉更好,不戴它看起来很奇怪,并且允许在您不希望它打开的情况下使用它

  • Remus Rusanu有一个similar shell使用保存点。我更喜欢原子数据库调用,并且不像他们的文章那样使用部分更新

关于sql-server-2005 - 包含 TRY CATCH ROLLBACK 模式的嵌套存储过程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2073737/

相关文章:

sql-server-2005 - 如何使用 T-SQL 将字符串转换为日期时间?

c# - 在 SQL Server 中存储整数值数组

sql - 排名超过切碎的 xml

sql - 实现分页的有效方法

c# - Linq to SQL C# 获取所有子/子子类别中的产品太慢

xml - SQL Server 2005 FOR XML 限制

.net - LINQ to SQL : how to update the only field without retrieving whole entity

sql - 调用存储过程时的性能问题

sql-server-2005 - SQL 存储过程使用 SELECT 设置变量

mysql - 我无法在 Mysql 过程中使用输入