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