如何在SQL Server的嵌套过程中处理事务?

时间:2019-02-18 08:46:00

标签: sql sql-server tsql

我有2个程序,即Proc1和Proc2。 我在proc2内部执行proc1。两个过程中都有多个DML操作。 proc1的输出在proc2中用于DML操作。 如果proc2中发生错误,则  如何在两个proc中处理事务以回滚所有DML操作?

我应该在两个proc中都写事务吗?

3 个答案:

答案 0 :(得分:1)

我们使用基于http://www.sommarskog.se/error_handling/Part1.html的通用错误处理程序,在适用的情况下,我们会将其包含在(嵌套的)交易中,以确保对链进行正确的管理:

CREATE PROCEDURE [dbo].[sp_ErrorHandler](@caller VARCHAR(255))
AS BEGIN
    SET NOCOUNT ON;
    DECLARE @errmsg NVARCHAR(2048), @severity TINYINT, @state TINYINT, @errno INT, @lineno INT;
    SELECT @errmsg=REPLACE(ERROR_MESSAGE(), 'DatabaseException: ', 'DatabaseException: '+QUOTENAME(@caller)+' --> ')
         , @severity=ERROR_SEVERITY()
         , @state=ERROR_STATE()
         , @errno=ERROR_NUMBER()
         , @lineno=ERROR_LINE();
    IF @errmsg NOT LIKE 'DatabaseException%' BEGIN
        SELECT @errmsg=N'DatabaseException: '+QUOTENAME(@caller)+N', Line '+LTRIM(STR(@lineno))+N', Error '+LTRIM(STR(@errno))+N': '+@errmsg;
    END;
    RAISERROR('%s', @severity, @state, @errmsg);
END;

(在master数据库中编译并标记为系统过程)

我们按如下方式使用此错误处理程序。在演示中,我有一个使用事务的外部proc和内部proc。

CREATE PROCEDURE dbo.uspOuterProc
AS
  BEGIN
  SET NOCOUNT, XACT_ABORT ON;

  BEGIN TRY
  BEGIN TRANSACTION;

  EXEC dbo.uspInnerProc;

  PRINT 1;

  COMMIT;
  END TRY
  BEGIN CATCH
  IF @@trancount > 0
  ROLLBACK TRANSACTION;

  EXEC master.dbo.sp_ErrorHandler @caller = 'dbo.uspOuterProc';
  END CATCH;
  END;
GO

CREATE PROCEDURE dbo.uspInnerProc
AS
  BEGIN
  SET NOCOUNT, XACT_ABORT ON;

  BEGIN TRY
  BEGIN TRANSACTION;

  PRINT 2;

  SELECT 1 / 0;

  PRINT 3;

  COMMIT;
  END TRY
  BEGIN CATCH
  IF @@trancount > 0
  ROLLBACK TRANSACTION;

  EXEC master.dbo.sp_ErrorHandler @caller = 'dbo.uspInnerProc';
  END CATCH;
  END;
GO

编译并运行后:

EXEC dbo.uspOuterProc

您应该得到以下结果:

2

Msg 50000, Level 16, State 1, Procedure sp_ErrorHandler, Line 13 [Batch Start Line 48]
DatabaseException: [dbo.uspOuterProc] --> [dbo.uspInnerProc], Line 12, Error 8134: Divide by zero error encountered.

答案 1 :(得分:0)

您可以在外部过程中处理事务(在您的情况下为proc2)。如果proc1中会发生任何错误,则proc2事务处理程序将对其进行处理。 假设不会直接调用proc1,它将在proc2内部调用。

答案 2 :(得分:0)

有3个基本的事务处理语句(还有一些我不愿提及的高级语句):

  • BEGIN TRANSACTION :将@@TRANCOUNT会话变量提高1。如果它从0变到1,则表示事务开始。任何大于1的值都将使同一笔交易继续进行。
  • COMMIT :将@@TRANCOUNT会话变量降低1。如果它从1变为0,则该交易被标记为已完成,并将影响自此以来所做的所有更改它是第一次创建的。
  • ROLLBACK :只要将@@TRANCOUNT会话变量的值至少设置为1或更高,就将其减小为0(无论该值是多少)。这将关闭事务并还原自首次创建以来所做的所有更改。

嵌套交易是一堆BEGIN TRANSACTION语句的组合。完全落实事务并使更改永久生效的唯一点是,当COMMIT会将事务计数从1减少到0时。这意味着每个{{1 }}您像金字塔一样被处死。

检查以下示例:

COMMIT

当您有一个执行另一个SP的SP并都有它们的事务时,您唯一需要关心的就是BEGIN TRANSACTION错误并执行正确的BEGIN TRANSACTION SELECT @@TRANCOUNT -- 1 BEGIN TRANSACTION SELECT @@TRANCOUNT -- 2 COMMIT TRANSACTION SELECT @@TRANCOUNT -- 1 (no change is permanent yet, not even the last one) BEGIN TRANSACTION SELECT @@TRANCOUNT -- 2 ROLLBACK SELECT @@TRANCOUNT -- 0 (all changes were discarded) IF 有一个正在进行中的公开/活动交易(如果不是CATCH语句将失败,表明没有任何回滚内容。

一个非常基本的ROLLBACK如下所示:

ROLLBACK

如果您想深入研究在SQL Server上处理事务的最佳方法,可以阅读this post