SQL Server - транзакции откатываются при ошибке?
У нас есть клиентское приложение, которое выполняет SQL на SQL Server 2005, например:
BEGIN TRAN;
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
COMMIT TRAN;
Он отправляется одной длинной строковой командой.
В случае сбоя одной из вставок или сбоя какой-либо части команды SQL Server откатывает транзакцию? Если он не откатывается, нужно ли отправлять вторую команду для его отката?
Я могу дать конкретную информацию о API и языке, который я использую, но я думаю, что SQL Server должен отвечать одинаково для любого языка.
6 ответов
Вы можете положить set xact_abort on
перед вашей транзакцией, чтобы убедиться, что sql автоматически откатывается в случае ошибки.
Вы правы в том, что вся транзакция будет откатана. Вы должны выполнить команду, чтобы откатить его.
Вы можете обернуть это в TRY CATCH
блок следующим образом
BEGIN TRY
BEGIN TRANSACTION
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
COMMIT TRAN -- Transaction Success!
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRAN --RollBack in case of Error
-- you can Raise ERROR with RAISEERROR() Statement including the details of the exception
RAISERROR(ERROR_MESSAGE(), ERROR_SEVERITY(), 1)
END CATCH
Вот код с получением сообщения об ошибке при работе с MSSQL Server 2016:
BEGIN TRY
BEGIN TRANSACTION
-- Do your stuff that might fail here
COMMIT
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRAN
DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE()
DECLARE @ErrorSeverity INT = ERROR_SEVERITY()
DECLARE @ErrorState INT = ERROR_STATE()
-- Use RAISERROR inside the CATCH block to return error
-- information about the original error that caused
-- execution to jump to the CATCH block.
RAISERROR (@ErrorMessage, -- Message text.
@ErrorSeverity, -- Severity.
@ErrorState -- State.
);
END CATCH
Из статьи MDSN " Управление транзакциями ( компонент Database Engine)".
Если в пакете возникает ошибка оператора времени выполнения (например, нарушение ограничения), по умолчанию в компоненте Database Engine выполняется откат только оператора, который сгенерировал ошибку. Вы можете изменить это поведение, используя инструкцию SET XACT_ABORT. После выполнения команды SET XACT_ABORT ON любая ошибка оператора во время выполнения вызывает автоматический откат текущей транзакции. SET XACT_ABORT не влияет на ошибки компиляции, такие как синтаксические ошибки. Для получения дополнительной информации см. SET XACT_ABORT (Transact-SQL).
В вашем случае он откатит всю транзакцию, если какая-либо из вставок не удалась
В случае сбоя одной из вставок или сбоя какой-либо части команды сервер SQL откатывает транзакцию?
Нет.
Если он не откатывается, нужно ли отправлять вторую команду для его отката?
Конечно, вы должны выпустить ROLLBACK
вместо COMMIT
,
Если вы хотите решить, следует ли совершать или откатывать транзакцию, вы должны удалить COMMIT
предложение из утверждения, проверьте результаты вставок, а затем выдать либо COMMIT
или же ROLLBACK
в зависимости от результатов проверки.
Используя этот метод в качестве альтернативы, вы также можете записать номер ошибки после каждого оператора, а затем использовать оператор if, чтобы определить, следует ли фиксировать или откатывать. Принятый ответ — лучший однострочный ответ, но если вы хотите узнать больше о возникшей проблеме, а не просто откатывать ее, вы можете использовать приведенный ниже пример и добавить дополнительную информацию, чтобы увидеть проблему. Конечно, вы также можете выполнить блок Try-Catch с помощьюRAISERROR
.
Вот пример того, что у меня есть на скорую руку:
DECLARE @errorNumber int;
BEGIN TRANSACTION;
INSERT INTO [table2] ([field1], [field2])
SELECT [fieldA], [fieldB]
FROM [table1];
SET @errorNumber = @@ERROR;
UPDATE [table3]
SET [field1] =
(SELECT COUNT(ID)
FROM [table2]
WHERE [table2].[fieldA] = [table3].[field2])
WHERE [field1] IS NULL;
SET @errorNumber = @@ERROR;
IF @errorNumber = 0
COMMIT TRANSACTION;
ELSE
BEGIN
ROLLBACK TRANSACTION;
PRINT CONCAT('Transaction rolled back with error number: ',@errorNumber);
END