如何自动回滚失败的 (SSMS) SQL 脚本中的所有“批次”

How to automatically rollback all “batches” in a (SSMS) SQL script that fails

我有 SQL 个脚本,它们将通过 SQL Server Management Studio 在 SQL Server 2012 数据库上执行。使用 GO 语句将脚本分成多个批次。如果任何批次中的任何更新出错,我希望一切都回滚。

我已经尝试使用 XACT_ABORT ON,但它没有像我预期的那样工作:

begin transaction txn
go
set xact_abort on
go
insert into Table1 (Col1) values (1)
go
insert into Table1 (Col1) values (2/0)
go
insert into Table1 (Col1) values (3)
go
create procedure Proc1
as
 begin
    select * from Table1
 end
go
commit transaction txn
go

当运行此脚本时,插入#2 失败并回滚插入#1。但是,插入 #3 成功并创建了存储过程。

有没有办法让一个插入失败,所有插入都失败?

请注意,我的实际脚本可能包含数百个批次,其中一些批次有数百种不同类型的更新(插入、更新、更改、删​​除等)。因此,我宁愿不必向批处理中添加任何额外的代码,即我想将它们全部打包在一个大事务中。

感谢任何建议。

我就是这样做的,它不会像你想象的那么简单,但它对我有用。

我在脚本中做的第一件事是:

IF (SELECT OBJECT_ID('tempdb..#tmpErrors')) IS NOT NULL DROP TABLE #tmpErrors
GO
CREATE TABLE #tmpErrors (Error int)
GO
SET XACT_ABORT ON
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
GO
BEGIN TRANSACTION
GO

然后insert/updates。每次操作后执行此操作:

GO
IF @@ERROR <> 0 AND @@TRANCOUNT > 0 BEGIN ROLLBACK;END
IF @@TRANCOUNT = 0 BEGIN INSERT  INTO #tmpErrors (Error) VALUES (1); BEGIN TRANSACTION;END
GO

然后在脚本的最后添加:

GO
IF EXISTS (SELECT * FROM #tmpErrors) ROLLBACK TRANSACTION
GO
IF @@TRANCOUNT>0 BEGIN
PRINT N'The transacted portion of the database update succeeded.'
COMMIT TRANSACTION
END
ELSE PRINT N'The transacted portion of the database update failed.'
GO
DROP TABLE #tmpErrors
GO

将所有这些应用到您正在尝试的脚本 运行 将是这样的:

-- this on top 
IF (SELECT OBJECT_ID('tempdb..#tmpErrors')) IS NOT NULL DROP TABLE #tmpErrors
GO
CREATE TABLE #tmpErrors (Error int)
GO
SET XACT_ABORT ON
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
GO
BEGIN TRANSACTION
GO

insert into Table1 (Col1) values (1)
GO
IF @@ERROR <> 0 AND @@TRANCOUNT > 0 BEGIN ROLLBACK;END
IF @@TRANCOUNT = 0 BEGIN INSERT  INTO #tmpErrors (Error) VALUES (1); BEGIN TRANSACTION;END
GO

insert into Table1 (Col1) values (2/0)
GO
IF @@ERROR <> 0 AND @@TRANCOUNT > 0 BEGIN ROLLBACK;END
IF @@TRANCOUNT = 0 BEGIN INSERT  INTO #tmpErrors (Error) VALUES (1); BEGIN TRANSACTION;END
GO

insert into Table1 (Col1) values (3)
GO
IF @@ERROR <> 0 AND @@TRANCOUNT > 0 BEGIN ROLLBACK;END
IF @@TRANCOUNT = 0 BEGIN INSERT  INTO #tmpErrors (Error) VALUES (1); BEGIN TRANSACTION;END
GO

-- this at the end 
GO
IF EXISTS (SELECT * FROM #tmpErrors) ROLLBACK TRANSACTION
GO
IF @@TRANCOUNT>0 BEGIN
PRINT N'The transacted portion of the database update succeeded.'
COMMIT TRANSACTION
END
ELSE PRINT N'The transacted portion of the database update failed.'
GO
DROP TABLE #tmpErrors
GO

下面是我如何使用 TRY/CATCH 块来做到这一点。您可以在此处阅读更多相关信息。 https://msdn.microsoft.com/en-us/library/ms175976.aspx

begin transaction

begin try
    insert into Table1 (Col1) values (1);
    insert into Table1 (Col1) values (2/0);
    insert into Table1 (Col1) values (3);
    commit transaction;
end try
begin catch
    select ERROR_NUMBER() AS ErrorNumber
        , ERROR_MESSAGE() AS ErrorMessage;
    rollback transaction;
end catch;