SQL Server-вложенные транзакции в хранимой процедуре

предположим, что это ситуация:

  [Stored Proc 1]
  BEGIN
     BEGIN TRANSACTION
       ...
            exec sp 2   
     COMMIT
  END

теперь, если SP 2-откатывается по какой - либо причине, делает ли SP 1-фиксацию или откат или исключение?

спасибо.

6 ответов


в SQL Server Нет автономных транзакций. Вы можете увидеть @@TRANCOUNT увеличение за 1, но откат влияет на все это.

редактировать попросил указать на документацию. Не знаю темы, которая документирует это явно, но я могу показать ее вам в действии.

USE tempdb;
GO

внутренний proc:

CREATE PROCEDURE dbo.sp2
    @trip BIT
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRANSACTION;

    PRINT @@TRANCOUNT;

    IF @trip = 1
    BEGIN
        IF @@TRANCOUNT > 0
            ROLLBACK TRANSACTION;
    END
    ELSE
    BEGIN   
        IF @@TRANCOUNT > 0
            COMMIT TRANSACTION;
    END

    PRINT @@TRANCOUNT;
END
GO

внешний proc:

CREATE PROCEDURE dbo.sp1
    @trip BIT
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRANSACTION;

    PRINT @@TRANCOUNT;

    BEGIN TRY
        EXEC dbo.sp2 @trip = @trip;
    END TRY
    BEGIN CATCH
        PRINT ERROR_MESSAGE();
    END CATCH

    PRINT @@TRANCOUNT;

    IF @@TRANCOUNT > 0
        COMMIT TRANSACTION;

    PRINT @@TRANCOUNT;
END
GO

так что теперь давайте назовем это и пусть все commit:

EXEC dbo.sp1 @trip = 0;

результаты:

1
2
1
1
0

теперь давайте назовем это и откатим внутреннюю процедуру:

EXEC dbo.sp1 @trip = 1;

результаты:

1
2
0 количество транзакций после выполнения указывает на несоответствие числа начать и commit. Предыдущий счет = 1, текущий счет = 0.
0
0


возможно для работы сделанной SP2 быть свернутым назад и не потерять работу сделанную SP1. Но для этого вы должны написать свои хранимые процедуры, используя очень конкретный шаблон, как описано в обработка исключений и вложенные транзакции:

create procedure [usp_my_procedure_name]
as
begin
    set nocount on;
    declare @trancount int;
    set @trancount = @@trancount;
    begin try
        if @trancount = 0
            begin transaction
        else
            save transaction usp_my_procedure_name;

        -- Do the actual work here

lbexit:
        if @trancount = 0   
            commit;
    end try
    begin catch
        declare @error int, @message varchar(4000), @xstate int;
        select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
        if @xstate = -1
            rollback;
        if @xstate = 1 and @trancount = 0
            rollback
        if @xstate = 1 and @trancount > 0
            rollback transaction usp_my_procedure_name;

        raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
    end catch   
end

не все ошибки могут быть восстановлены, существует ряд условий ошибок, из которых транзакция не может восстановиться, наиболее очевидным примером является взаимоблокировка (вы уведомлены об исключении взаимоблокировки после транзакция уже откатилась). И SP1, и SP@ должны быть написаны с использованием этого шаблона. Если у вас есть rogue SP, или вы хотите просто использовать существующие хранимые процедуры, которые волей-неволей выдают ROLLBACK отчетность тогда ваше дело проиграно.


Если SP2 откатывает транзакцию, SP1 также откатится.

см.:http://msdn.microsoft.com/en-US/library/ms187844 (v=sql.105).aspx для деталей.


во вложенных транзакциях, если какая-либо из внутренних транзакций откатывается, все ее внешние транзакции будут откатываться.


вот быстрый и грязный способ вложить транзакции в хранимые процедуры (используя код из ответа Аарона), который иногда может быть полезен. Он использует параметр по умолчанию, чтобы указать внутренней процедуре, если это вложенный вызов, и возвращает результат успеха/сбоя внешней процедуре.

CREATE PROCEDURE dbo.sp2
    @trip BIT,
    @nested BIT = 0
AS
BEGIN
    SET NOCOUNT, XACT_ABORT ON

    IF @nested = 0 BEGIN TRAN

    PRINT @@TRANCOUNT

    IF @trip = 1
    BEGIN
        IF @nested = 0 ROLLBACK
        RETURN 1
    END
    ELSE
    BEGIN   
        IF @nested = 0 COMMIT
    END

    PRINT @@TRANCOUNT
    RETURN 0
END
GO

внешняя процедура проверяет успешность / неудачу отката транзакции, если это необходимо.

CREATE PROCEDURE dbo.sp1
    @trip BIT
AS
BEGIN
    DECLARE @result INT
    SET NOCOUNT, XACT_ABORT ON

    BEGIN TRAN

    PRINT @@TRANCOUNT

    BEGIN TRY
        EXEC @result = dbo.sp2 @trip = @trip, @nested = 1
        IF @result <> 0
        BEGIN
            ROLLBACK
            RETURN 1
        END
    END TRY
    BEGIN CATCH
        PRINT ERROR_MESSAGE()
    END CATCH

    PRINT @@TRANCOUNT

    COMMIT

    PRINT @@TRANCOUNT
    RETURN 0
END
GO

USE [DemoProject]
GO

/****** Object:  StoredProcedure [dbo].[Customers_CRUD]    Script Date: 11-Jan-17 2:57:38 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE [dbo].[Customers_CRUD]
      @Action VARCHAR(10)
      ,@BId INT = NULL
      ,@Username VARCHAR(50) = NULL
      ,@Provincename VARCHAR(50) = NULL
      ,@Cityname VARCHAR(50) = NULL
      ,@Number VARCHAR(50) = NULL
      ,@Name VARCHAR(50) = NULL
      ,@ContentType VARCHAR(50) = NULL
      ,@Data VARBINARY(MAX) = NULL

AS
BEGIN
      SET NOCOUNT ON;

      --SELECT
    IF @Action = 'SELECT'
      BEGIN
            SELECT BId , Username,Provincename,Cityname,Number,Name,ContentType, Data
            FROM tblbooking
      END

      --INSERT
    IF @Action = 'INSERT'
      BEGIN
            INSERT INTO tblbooking(Username,Provincename,Cityname,Number,Name,ContentType, Data)
            VALUES (@Username ,@Provincename ,@Cityname ,@Number ,@Name ,@ContentType ,@Data)
      END

      --UPDATE
    IF @Action = 'UPDATE'
      BEGIN
            UPDATE tblbooking
            SET Username = @Username,Provincename = @Provincename,Cityname = @Cityname,Number = @Number,Name = @Name,ContentType = @ContentType,Data = @Data
            WHERE BId = @BId
      END

      --DELETE
    IF @Action = 'DELETE'
      BEGIN
            DELETE FROM tblbooking
            WHERE BId = @BId
      END
END

GO