В чем выгода использования 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 при нарушении PK.
Цитирование MSDN :
Когда SET XACT_ABORT установлен в ON, если инструкция Transact-SQL вызывает ошибку во время выполнения, вся транзакция завершается и откатывается. Когда SET XACT_ABORT имеет значение OFF, в некоторых случаях откатывается только инструкция Transact-SQL, которая вызвала ошибку, и транзакция продолжает обрабатываться.
На практике это означает, что некоторые операторы могут потерпеть неудачу, оставив транзакцию «частично завершенной», и для вызывающей стороны может не быть никаких признаков этого сбоя.
Простой пример:
INSERT INTO t1 VALUES (1/0)
INSERT INTO t2 VALUES (1/1)
SELECT 'Everything is fine'
Этот код будет выполнен «успешно» с выключенным XACT_ABORT и завершится с ошибкой с XACT_ABORT ON («INSERT INTO t2» не будет выполнен, и клиентское приложение вызовет исключение).
В качестве более гибкого подхода вы можете проверять @@ ERROR после каждого оператора (старая школа) или использовать блоки TRY ... CATCH (MSSQL2005 +). Лично я предпочитаю устанавливать XACT_ABORT ON всякий раз, когда нет причин для какой-либо сложной обработки ошибок.
Что касается тайм-аутов клиентов и использования XACT_ABORT для их обработки, по моему мнению, есть, по крайней мере, одна очень веская причина иметь тайм-ауты в клиентских API, таких как SqlClient, и это защищать код клиентского приложения от тупиков, возникающих в коде SQL-сервера. В этом случае в клиентском коде нет ошибки, но он должен сам защитить себя от блокировки, ожидающей завершения команды на сервере. И наоборот, если для защиты клиентского кода должны существовать тайм-ауты клиента, то XACT_ABORT ON должен защищать код сервера от аварий клиента, в случае, если выполнение кода сервера занимает больше времени, чем клиент готов ждать.
Он используется в управлении транзакциями, чтобы гарантировать, что любые ошибки приведут к откату транзакции.