嵌套存储过程包含TRY CATCH ROLLBACK模式?

Dav*_*vid 54 stored-procedures transactions sql-server-2005 linq-to-sql

我对以下模式的副作用和潜在问题很感兴趣:

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
Run Code Online (Sandbox Code Playgroud)

据我所知,当使用单个过程时,此模式是合理的 - 过程将完成所有语句而不会出错,或者它将回滚所有操作并报告错误.

但是,当一个存储过程调用另一个存储过程来执行某个子工作单元时(理解为较小的过程有时会单独调用),我看到与回滚有关的问题 - 一条信息性消息(级别16)发表声明The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION..我假设是因为子过程中的回滚总是回滚最外层事务,而不仅仅是子过程中启动的事务.

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

最后是客户端(Linq2SQL),它有自己的事务层:

try
{
    var context = new MyDataContext();
    using (var transaction = new TransactionScope())
    {       
            // Some Linq stuff
        context.SubmitChanges();
        context.MyStoredProcedure();
        transactionComplete();
    }
}
catch
{
    // An error occured!
}
Run Code Online (Sandbox Code Playgroud)

在一个存储过程,"MySubProcedure",称该事件 MyStoredProcedure引发错误,我可以肯定的是以前在MyStoredProcedure所做的一切都会被回滚,所有的SubmitChanges作出的LINQ的操作将被回滚,最后的会记录错误吗?或者我需要在模式中进行哪些更改以确保整个操作是原子操作,同时仍允许子部件单独使用(即子程序仍应具有相同的原子保护)

gbn*_*gbn 106

这是我们的模板(删除了错误记录)

这是为了处理

说明:

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

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

    • BEGIN TRAN 增量 @@TRANCOUNT

    • COMMIT 递减 @@TRANCOUNT

    • ROLLBACK返回@@TRANCOUNT

  • 您不能减少@@TRANCOUNT当前范围
    这是您认为的"内部交易"

  • SET XACT_ABORT ON抑制由不匹配引起的错误266 @@TRANCOUNT
    并且还处理dba.se上的此类"SQL Server Transaction Timeout"之类的问题

  • 这允许客户端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
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 回滚检查实际上是多余的,因为SET XACT_ABORT ON.然而,它让我感觉更好,看起来很奇怪,并允许你不想要它的情况

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

  • 你应该总是在imho上使用SET NOCOUNT. (7认同)

Aar*_*and 11

我不是Linq的家伙(也不是Erland),但他写了关于错误处理的绝对圣经.除了Linq可能会增加您的问题的复杂性之外,所有其他问题都应该在这里得到解答:

http://www.sommarskog.se/error_handling/Part1.html

(旧链接:http://www.sommarskog.se/error_handling_2005.html)