SQL Server - вложенные транзакции в хранимой процедуре - PullRequest
8 голосов
/ 14 марта 2012

Скажем так:

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

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

Спасибо.

Ответы [ 6 ]

8 голосов
/ 14 марта 2012

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

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

USE tempdb;
GO

Внутренний процесс:

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

Внешний процесс:

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

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

EXEC dbo.sp1 @trip = 0;

Результаты:

1
2
1
1
0

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

EXEC dbo.sp1 @trip = 1;

Результаты:

1
2
0 <- обратите внимание, что откат здесь откатил оба <br>Число транзакций после EXECUTE указывает на несовпадающее количество операторов BEGIN и COMMIT.Предыдущий счет = 1, текущий счет = 0.
0
0

6 голосов
/ 14 марта 2012

Работа, выполненная пакетом обновления 2, может быть отменена и не потерять работу, выполненную пакетом обновления 1 (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 @ должны быть написаны с использованием этого шаблона. Если у вас мошеннический SP или вы хотите просто использовать существующие хранимые процедуры, которые произвольно выдают ROLLBACK операторов, то ваша причина потеряна.

1 голос
/ 14 марта 2012

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

Подробнее см. http://msdn.microsoft.com/en-US/library/ms187844(v=sql.105).aspx.

0 голосов
/ 11 января 2017
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
0 голосов
/ 19 октября 2013

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

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
0 голосов
/ 07 сентября 2012

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...