在使用 TRY CATCH 的 SQL Server 中提交事务的最佳实践

use*_*358 5 sql-server t-sql transaction exception code

在 SQL Server 代码块中,放置提交事务的最佳位置是什么?在 try catch 块内部还是外部?

例如,选项 A 或选项 B 是正确的方法还是主观选择?

选项A

CREATE PROCEDURE DummyProc 
BEGIN TRY
      BEGIN TRANSACTION
      INSERT sometable(a, b) VALUES (@a, @b)
      INSERT sometable(a, b) VALUES (@b, @a)
      COMMIT TRANSACTION
   END TRY
   BEGIN CATCH
      IF @@trancount > 0 ROLLBACK TRANSACTION
      DECLARE @msg nvarchar(2048) = error_message()  
      RAISERROR (@msg, 16, 1)
      RETURN 55555
   END CATCH   
Run Code Online (Sandbox Code Playgroud)

选项B

CREATE PROCEDURE DummyProc 
BEGIN TRY
      BEGIN TRANSACTION
      INSERT sometable(a, b) VALUES (@a, @b)
      INSERT sometable(a, b) VALUES (@b, @a)

   END TRY
   BEGIN CATCH
      IF @@trancount > 0 ROLLBACK TRANSACTION
      DECLARE @msg nvarchar(2048) = error_message()  
      RAISERROR (@msg, 16, 1)
      RETURN 55555
   END CATCH
   IF @@trancount > 0  COMMIT TRANSACTION
Run Code Online (Sandbox Code Playgroud)

在选项B中,当在块外进行提交时是否有可能发生一些错误TRY-CATCH

Geo*_*ios 7

我发现执行此操作的最佳方法是以下代码:

SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION

    /*
        Code goes here
    */

    COMMIT TRANSACTION
END TRY
BEGIN CATCH

    DECLARE @ErrorMessage NVARCHAR(4000);
    DECLARE @ErrorSeverity INT;
    DECLARE @ErrorState INT;

    SELECT 
        @ErrorMessage = ERROR_MESSAGE(),
        @ErrorSeverity = ERROR_SEVERITY(),
        @ErrorState = ERROR_STATE();

    RAISERROR (@ErrorMessage, -- Message text.
               @ErrorSeverity, -- Severity.
               @ErrorState -- State.
               );

    -- If >= SQL 2012 replace all code in catch block above with
    -- THROW;

    WHILE @@TRANCOUNT > 0
    BEGIN
        ROLLBACK TRANSACTION;
    END

END CATCH
Run Code Online (Sandbox Code Playgroud)

请注意,使用 XACT_ABORT 来确保有效捕获错误,BEGIN 和 COMMIT 语句都位于 TRY 块内,以及 @@Trancount 的 WHILE - 这应该确保回滚嵌套事务(并不总是适用)

对于 2012 以上的 SQL 版本,THROW 语句还可以替换 RAISERROR,以重新抛出捕获的异常/错误。

正如 Dan Guzman 在他的评论中指出的那样,XACT_ABORT 对于捕获 TRY/CATCH 构造不会捕获的错误非常有用,包括超时、运行时排序错误等


Han*_*dyD 4

选项A是正确的选择。有可能事务中的所有语句都有效,然后实际的 COMMIT 失败,因此您将 COMMIT 保留在 TRY 块内,以便捕获 COMMIT 的任何失败,并且您可以优雅地处理此错误并回滚。

请参阅SE 上的这个答案。在您的示例代码中,如果我们假设其他用户同时运行此过程,则 TRY 块可能会成功完成,然后由于其他会话所做的更改而在 COMMIT 上失败。

  • 我同意并添加将“SET XACT_ABORT ON”添加到过程的开头,以便如果发生客户端超时(其中 CATCH 块不会被执行),事务将立即回滚。 (5认同)

归档时间:

查看次数:

20530 次

最近记录:

6 年,5 月 前