Каково влияние включения / выключения XACT_ABORT в хранимых процедурах родитель / потомок соответственно?
Я пытаюсь улучшить обработку ошибок текущей системы, чтобы производить более значимые сообщения об ошибках. У меня есть "корневая" хранимая процедура, которая выполняет несколько вызовов других вложенных хранимых процедур.
В корне зр, XACT_ABORT
установлен в ON
но во вложенных процедурах, XACT_ABORT
установлен в OFF
, Я хочу фиксировать конкретные ошибки процедур более низкого уровня, а не получать ошибку корневой процедуры.
Я часто вижу ошибку, uncommittable transaction is detected at the end of the batch, the transaction is being rolled back.
Есть ли какой-либо эффект от наличия этих "смешанных" сред с XACT_ABORTs
?
Кроме того, если у вас есть какие-либо предложения по расширенной обработке ошибок, это будет высоко ценится. Я думаю, что я хотел бы использовать sp_executesql
так что я могу передать параметры, чтобы получить вывод ошибок без необходимости изменять все хранимые процедуры и использовать RAISERROR
вызвать родительскую процедуру CATCH
блок.
2 ответа
Согласно ответу Андомара здесь и MSDN:
Настройка SET XACT_ABORT устанавливается во время выполнения или выполнения, а не во время анализа
т.е. XACT_ABORT
не будет "копироваться" из сеанса создания в каждую процедуру, поэтому любой PROC, который явно не установит этот параметр внутри, будет наследовать этот параметр из окружающего сеанса во время выполнения, что может иметь катастрофические последствия.
FWIW, как правило, мы всегда гарантируем, что XACT_ABORT
включен в глобальном масштабе и выполните проверку на предмет отсутствия дефектов, чтобы убедиться, что ни один из наших PROC не изменил этот параметр.
Обратите внимание, что XACT_ABORT
однако, это не серебряная пуля - например, ошибки, которые были вызваны вашим PROC с RAISERROR, не прекратят выполнение пакета. Тем не менее, кажется, что это улучшается с помощью ключевого слова THROW в SQL 2012
Как вы и предполагали, и согласно наблюдению Ремуса Русану, структурированная обработка исключений (TRY / CATCH) является гораздо более чистым и надежным механизмом обработки исключений.
Способ сохранить XACT_ABORT и получать ошибки, если они есть, или совершать, если все нормально при вызове SP, который может вызвать другой SP: два теста sp и три в качестве примера
create PROCEDURE [dbo].[myTestProcCalled]
(
@testin int=0
)
as
begin
declare @InnerTrans int
set XACT_ABORT on;
set @InnerTrans = @@trancount;
PRINT '02_01_Trancount='+cast (@InnerTrans as varchar(2));
begin try
if (@InnerTrans = 0)
begin
PRINT '02_02_beginning trans';
begin transaction
end
declare @t2 int
set @t2=0
PRINT '02_03_doing division'
set @t2=10/@testin
PRINT '02_04_doing AfterStuff'
if (@InnerTrans = 0 and XACT_STATE()=1)
begin
PRINT '02_05_Committing'
commit transaction
end
PRINT '02_05B_selecting calledValue=' +cast(@t2 as varchar(20))
select @t2 as insidevalue
end try
begin catch
PRINT '02_06_Catching Errors from called'
declare @ErrorMessage nvarchar(4000);
declare @ErrorNumber int;
declare @ErrorSeverity int;
declare @ErrorState int;
select @ErrorMessage = error_message(), @ErrorNumber = error_number(), @ErrorSeverity = error_severity(), @ErrorState = error_state();
if (@InnerTrans = 0 and XACT_STATE()=-1)
begin
PRINT '02_07_Rolbacking'
rollback transaction
end
PRINT '02_08_Rising Error'
raiserror(@ErrorMessage, @ErrorSeverity, @ErrorState);
--use throw if in 2012 or above
-- else might add a "return" statement
end catch
end
go
create PROCEDURE [dbo].[myTestPCalling]
(
@test int=0
,@testinside int=0
)
as
begin
declare @InnerTrans int
set XACT_ABORT on;
set @InnerTrans = @@trancount;
PRINT '01_01_Trancount='+cast (@InnerTrans as varchar(2));
begin try
if (@InnerTrans = 0)
begin
PRINT '01_02_beginning trans';
begin transaction
end
declare @t2 int
set @t2=0
PRINT '01_03_doing division'
set @t2=10/@test
PRINT '01_04_calling inside sp'
execute [dbo].[myTestProcCalled]
@testin = @testinside
--
PRINT '01_05_doing AfterStuff'
if (@InnerTrans = 0 and XACT_STATE()=1)
begin
PRINT '01_06_Committing'
commit transaction
PRINT '01_06B_selecting callerValue=' +cast(@t2 as varchar(20))
select @t2 as outsidevalue
end
end try
begin catch
PRINT '01_07_Catching Errors from Caller'
declare @ErrorMessage nvarchar(4000);
declare @ErrorNumber int;
declare @ErrorSeverity int;
declare @ErrorState int;
select @ErrorMessage = error_message(), @ErrorNumber = error_number(), @ErrorSeverity = error_severity(), @ErrorState = error_state();
if (@InnerTrans = 0 and XACT_STATE()=-1)
begin
PRINT '01_08_Rolbacking'
rollback transaction
end
PRINT '01_09_Rising Error'
raiserror(@ErrorMessage, @ErrorSeverity, @ErrorState);
--use throw if in 2012 or above
-- else might add a "return" statement
end catch
end
----test 1 :result OK----
USE [PRO-CGWEB]
GO
DECLARE @return_value int
EXEC @return_value = [dbo].[myTestPCalling]
@test =2
,@testinside = 2
SELECT 'Return Value' = @return_value
GO
----test2 :error in caller ----
USE [PRO-CGWEB]
GO
DECLARE @return_value int
EXEC @return_value = [dbo].[myTestPCalling]
@test =0
,@testinside = 2
SELECT 'Return Value' = @return_value
GO
----test3 :error in calling ----
USE [PRO-CGWEB]
GO
DECLARE @return_value int
EXEC @return_value = [dbo].[myTestPCalling]
@test =2
,@testinside = 0
SELECT 'Return Value' = @return_value
GO