Откат транзакции SQL Server из-за ошибки вне транзакции - PullRequest
0 голосов
/ 09 октября 2018

У меня есть оператор TRY CATCH, в котором есть некоторый код в транзакции в TRY и другой код вне транзакции в TRY.Если код за пределами преобразования (все еще в TRY) выдает ошибку, моя транзакция не откатывается в CATCH.Однако если я удалю код за пределами транзакции, в случае ошибки транзакция будет откатана.Почему это ведет себя так?SQL Server 2014.

Мой код.Если удаленный процесс выдает ошибку, транзакция не будет откатана:

BEGIN  try
begin transaction

  INSERT INTO bos_south_florida_job_map (job_id, original_job_id, 
created_date, updated_date,completed_status_sent_ind, 
assigned_status_sent_ind, status_prev)
VALUES (9999, '1234', getdate(), getdate(),0,0,'CREATED');


COMMIT TRANSACTION

declare @sql varchar(max)

set @sql = ''

select @sql = '
   declare @error1 varchar(255),
   @error2 varchar(255),
   @error3 varchar(255)

   Exec NEXTGEN.DBO.wbAf_ConfirmDispatchedReservation ''AFFWEB'', ''A10596'', ''Admin'', ''Admin'', '''+cast(preassignedsubconcode as varchar(100))+''', '''+cast('1234' as varchar(20))+''', '''+convert(char(23),ISNULL(ScheduledDispatchDateTime,''),121)+''', '+cast('1234' as varchar(12))+',null,@error1 output,@error2 output,@error3 output

   if @error1 is not null or @error2 is not null or @error3 is not null
   begin
        set @error1 = @error2 + '' '' + @error3 + '' '' + @error1
        RAISERROR (@error1, 16, 1)
   end
   '
from [BCCUATWSQL290].NEXTGEN.DBO.tbRideResCurDispatch
where resno = '35002616'

   exec(@sql) at [BCCUATWSQL290]

END TRY

BEGIN CATCH

IF @@TRANCOUNT > 0  
ROLLBACK TRANSACTION

DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;

SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE()

INSERT INTO bos_south_florida_error_log (original_job_id, [action], 
error_datetime, [error_message])
SELECT
    '1234',
    'CREATE_JOB',
    GETDATE(),
    'Msg: ' + ISNULL(CONVERT(VARCHAR, ERROR_NUMBER()), 'N/A') + ', Level: ' + ISNULL(CONVERT(VARCHAR, @ErrorSeverity), 'N/A') + ', Line: ' + ISNULL(CONVERT(VARCHAR, ERROR_LINE()), 'N/A') + ', Error: ' + ISNULL(@ErrorMessage, 'N/A')


END CATCH

Это приведет к откату транзакции:

BEGIN  try
begin transaction

  INSERT INTO bos_south_florida_job_map (job_id, original_job_id, 
created_date, updated_date,completed_status_sent_ind, 
assigned_status_sent_ind, status_prev)
VALUES (9999, '1234', getdate(), getdate(),0,0,'CREATED');

RAISERROR ('BLAH', 16, 1)

COMMIT TRANSACTION

END TRY

BEGIN CATCH

IF @@TRANCOUNT > 0  
ROLLBACK TRANSACTION

DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;

SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE()

INSERT INTO bos_south_florida_error_log (original_job_id, [action], 
error_datetime, [error_message])
SELECT
    '1234',
    'CREATE_JOB',
    GETDATE(),
    'Msg: ' + ISNULL(CONVERT(VARCHAR, ERROR_NUMBER()), 'N/A') + ', Level: ' + ISNULL(CONVERT(VARCHAR, @ErrorSeverity), 'N/A') + ', Line: ' + ISNULL(CONVERT(VARCHAR, ERROR_LINE()), 'N/A') + ', Error: ' + ISNULL(@ErrorMessage, 'N/A')


END CATCH

1 Ответ

0 голосов
/ 09 октября 2018

Напомним, что каждый Begin Transaction увеличивает @@Trancount на 1, а каждый Commit уменьшает его на 1.

В вашем первом примере транзакция уже зафиксирована и @@TranCount уменьшается до нуля, прежде чемвы выбрасываете ошибку, поэтому в предложении Catch, @@Trancount будет равно нулю.Это хорошо.Совершенная транзакция уже является историей, ее нельзя откатить.

Во втором примере, когда вы поднимаете ошибку после Begin, но до Commit, тогда @@TranCount равно 1, когда вы достигаетеCatch, и вы выполняете откат.

Предположительно, вы хотите, чтобы ошибка при удаленном вызове вызывала откат?Этого вы можете достичь, переместив Commit после удаленного вызова, сделав это последним оператором до End Try.

Обратите внимание, что межсерверные транзакции относительно дороги и требуют выполнения MS DTC на обоих серверах.Администраторы базы данных могут нахмуриться при этом на сильно загруженных серверах.

Кстати, транзакция вокруг одной вставки обычно бессмысленна.Нормальным поведением является режим 'autocommit', что означает, что операторы вставки / обновления / удаления 'ограничены невидимым BEGIN TRANSACTION и невидимым оператором COMMIT TRANSACTION'

...