我有一个TSQL脚本可以进行大量的数据库结构调整,但是当事情发生故障时让它通过是不安全的。
说清楚:
我所拥有的是以下顺序
BEGIN TRANSACTION
ALTER Stuff
GO
CREATE New Stuff
GO
DROP Old Stuff
GO
IF @@ERROR != 0
BEGIN
PRINT 'Errors Found ... Rolling back'
ROLLBACK TRANSACTION
RETURN
END
ELSE
PRINT 'No Errors ... Committing changes'
COMMIT TRANSACTION
只是为了说明我正在使用的内容......不能详细说明 现在,问题......
当我引入错误(以测试事情是否被回滚)时,我得到一个声明,即ROLLBACK TRANSACTION找不到相应的BEGIN TRANSACTION。 这让我相信当真正错误并且交易已经被杀死时。 我还注意到,脚本没有完全退出错误,因此DID尝试在发生错误后执行每个语句。 (我注意到这一点,当我不期待它们出现新表时,因为它应该有回滚)
答案 0 :(得分:7)
发生错误时,事务将自动回滚,并且当前批次将中止。
然而,执行继续进入下一批。因此,错误执行后批处理中的所有内容。然后,当您稍后检查错误时,您尝试回滚已经回滚的事务。
此外,要停止整个脚本,而不仅仅是当前批次,您应该使用:
raiserror('Error description here', 20, -1) with log
有关该详细信息,请参阅my answer here。
所以你需要在每批后检查@error
,我认为这样的事情应该有效:
BEGIN TRANSACTION
GO
ALTER Stuff
GO
if @@error != 0 raiserror('Script failed', 20, -1) with log
GO
CREATE New Stuff
GO
if @@error != 0 raiserror('Script failed', 20, -1) with log
GO
DROP Old Stuff
GO
if @@error != 0 raiserror('Script failed', 20, -1) with log
GO
PRINT 'No Errors ... Committing changes'
COMMIT TRANSACTION
答案 1 :(得分:4)
尝试使用RETURN。这将立即退出脚本或过程,不会执行以下任何语句。您可以将它与BEGIN,ROLLBACK和COMMIT TRANSACTION语句结合使用来撤消任何数据损坏:
BEGIN
BEGIN TRANSACTION
<first batch>
IF @@error <> 0
begin
RAISERROR ('first batch failed',16,-1)
ROLLBACK TRANSACTION
RETURN
end
<second batch>
IF @@error <> 0
begin
RAISERROR ('second batch failed',16,-1)
ROLLBACK TRANSACTION
RETURN
end
PRINT 'WIN!'
COMMIT TRANSACTION
END
答案 2 :(得分:3)
我没有使用raiseerror解决方案,因为它失败,因为我没有管理员权限。我通过事务处理扩展了noexec开/关解决方案,如下所示:
set noexec off
begin transaction
go
<First batch, do something here>
go
if @@error != 0 set noexec on;
<Second batch, do something here>
go
if @@error != 0 set noexec on;
<... etc>
declare @finished bit;
set @finished = 1;
SET noexec off;
IF @finished = 1
BEGIN
PRINT 'Committing changes'
COMMIT TRANSACTION
END
ELSE
BEGIN
PRINT 'Errors occured. Rolling back changes'
ROLLBACK TRANSACTION
END
显然编译器“理解”IF中的@finished变量,即使出现错误并且执行被禁用。但是,仅当未禁用执行时,该值才设置为1。因此,我可以相应地提交或回滚事务。
答案 3 :(得分:0)
您可以尝试这样的事情......如果您正在使用Try块...错误级别16(或大多数应用程序错误)立即将控件传输到CATCH块而不在try中执行任何进一步的语句块...
Begin Transaction
Begin Try
-- Do your Stuff
If (@@RowCount <> 1) -- Error condition
Begin
Raiserror('Error Message',16,1)
End
Commit
End Try
Begin Catch
IF @@Trancount > 0
begin
Rollback Transaction
End
Declare @ErrMsg varchar(4000), @Errseverity int
SELECT @ErrMsg = ERROR_MESSAGE(),
@ErrSeverity = ERROR_SEVERITY()
RAISERROR(@ErrMsg, @ErrSeverity, 1)
End Catch
希望这会有所帮助......
答案 4 :(得分:0)
SET XACT_ABORT ON
BEGIN TRAN
-- Batch 1
GO
if @@TRANCOUNT = 0
SET NOEXEC ON;
GO
-- Batch 2
GO
if @@TRANCOUNT = 0
SET NOEXEC ON;
GO
-- Batch 3
GO
if @@TRANCOUNT > 0
COMMIT
GO