Яка користь від використання SET XACT_ABORT ON
в збереженій процедурі?
Яка користь від використання SET XACT_ABORT ON
в збереженій процедурі?
Відповіді:
SET XACT_ABORT ON
доручає SQL Server відкатати всю транзакцію та перервати партію, коли виникає помилка під час виконання. Він охоплює вас у випадках, як час очікування команди, що виникає на клієнтській програмі, а не в самому SQL Server (який не охоплюється XACT_ABORT OFF
налаштуваннями за замовчуванням .)
Оскільки час очікування запиту залишить транзакцію відкритою, SET XACT_ABORT ON
рекомендується в усіх збережених процедурах із явними транзакціями (якщо у вас немає конкретної причини зробити інше), оскільки наслідки програми, яка виконує роботу над з'єднанням з відкритою транзакцією, згубні.
У блозі Дена Гузмана дійсно чудовий огляд ,
BEGIN TRY
- BEGIN CATCH
і ROLLBACK
з BEGIN CATCH
блоком у Sql?
BEGIN TRY
- BEGIN CATCH
не вдасться зафіксувати такі випадки, як час очікування, що виникає на клієнтській програмі, і деякі помилки SQL теж неможливо побачити, залишаючи вам відкриту транзакцію, де її ви не очікували.
На мою думку, SET XACT_ABORT ON став застарілим додаванням BEGIN TRY / BEGIN CATCH у SQL 2k5. Перед блоками виключень у Transact-SQL було дуже важко обробляти помилки, а незбалансовані процедури були занадто поширеними (процедури, які мали інший @@ TRANCOUNT на виході порівняно з записом).
З додаванням Transact-SQL обробку винятків набагато простіше написати правильні процедури, які гарантовано правильно збалансувати транзакції. Наприклад, я використовую цей шаблон для обробки винятків та вкладених транзакцій :
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
go
Це дозволяє мені писати атомні процедури, які відкатують лише власну роботу у випадку помилок, що підлягають відновленню.
Однією з головних проблем, з якою стикаються процедури Transact-SQL, є чистота даних : іноді отримані параметри або дані в таблицях просто невірно, що призводить до повторюваних помилок ключів, помилок референсного обмеження, перевірки помилок обмеження тощо тощо. Зрештою, саме в цьому і полягає роль цих обмежень, якщо ці помилки чистоти даних були б неможливі, і всі вони потрапляли в ділову логіку, обмеження були б усі застарілими (для ефекту додається драматичне перебільшення). Якщо XACT_ABORT увімкнено, усі ці помилки призводять до втрати всієї транзакції, на відміну від можливості блокувати коди виключень, які обробляють виняток витончено. Типовим прикладом є спроба зробити INSERT та повернутися до UPDATE на порушення ПК.
Цитуючи MSDN :
Якщо SET XACT_ABORT увімкнено, якщо оператор Transact-SQL викликає помилку запуску, вся транзакція припиняється та повертається назад. Якщо SET XACT_ABORT ВИМКНЕНО, в деяких випадках відновлюється лише оператор Transact-SQL, який викликав помилку, і транзакція продовжується обробкою.
На практиці це означає, що деякі висловлювання можуть вийти з ладу, залишаючи транзакцію "частково завершеною", і для абонента може бути ніяких ознак цієї відмови.
Простий приклад:
INSERT INTO t1 VALUES (1/0)
INSERT INTO t2 VALUES (1/1)
SELECT 'Everything is fine'
Цей код буде виконано "успішно" з XACT_ABORT OFF, і закінчиться помилкою, коли XACT_ABORT ON ("INSERT INTO t2" не буде виконано, і клієнтська програма створить виняток).
Як більш гнучкий підхід, ви можете перевірити @@ ПОМИЛКУ після кожного твердження (стара школа) або використовувати блоки TRY ... CATCH (MSSQL2005 +). Особисто я вважаю за краще встановити XACT_ABORT ON, коли немає причин для деякого вдосконаленого поводження з помилками.
Що стосується тайм-аутів клієнта та використання XACT_ABORT для їх обробки, на мою думку, є хоча б одна дуже вагома причина мати тайм-аути в клієнтських API, таких як SqlClient, і це захистити код програми клієнта від тупиків, що виникають у коді сервера SQL. У цьому випадку клієнтський код не має жодних помилок, але повинен захистити його від блокування назавжди, очікуючи виконання команди на сервері. І навпаки, якщо для захисту клієнтського коду доводиться існувати тайм-аути клієнта, так і XACT_ABORT ON повинен захищати код сервера від перерви клієнта, у випадку, якщо серверний код потребує більше часу, ніж клієнт готовий чекати.