我们有在 SQL Server 2005 上运行某些 SQL 的客户端应用程序,如下所示:
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 应该对任何语言做出相同的响应。
您可以在事务前加上
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
-- <EDIT>: From SQL2008 on, you must raise error messages as follows:
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;
SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE();
RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
-- </EDIT>
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, @ErrorSeverity, @ErrorState);
END CATCH
来自 MDSN 文章,控制事务(数据库引擎)。
如果批处理中发生运行时语句错误(例如违反约束),数据库引擎中的默认行为是仅回滚生成错误的语句。您可以使用 SET XACT_ABORT 语句更改此行为。执行 SET XACT_ABORT ON 后,任何运行时语句错误都会导致当前事务自动回滚。编译错误(例如语法错误)不受 SET XACT_ABORT 的影响。有关详细信息,请参阅 SET XACT_ABORT (Transact-SQL)。
在您的情况下,当任何插入失败时,它将回滚整个事务。
如果其中一个插入失败,或者命令的任何部分失败,SQL Server 是否会回滚事务?
不,没有。
如果不回滚,我是否需要发送第二个命令来回滚?
当然,您应该发出
ROLLBACK
而不是 COMMIT
。
如果你想决定是提交还是回滚事务,你应该从语句中删除
COMMIT
语句,检查插入的结果,然后根据插入的结果发出 COMMIT
或 ROLLBACK
检查一下。
作为替代方法,您还可以捕获每个语句后的错误号,然后使用 if 语句来确定是提交还是回滚。接受的答案是最好的一句话,但如果您想更多地了解所遇到的问题,而不只是将其回滚,您可以使用下面的示例并添加一些其他信息来查看问题。当然,您也可以使用
RAISERROR
来执行 Try-Catch 块。
这是我所拥有的快速示例:
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