2010-05-26 25 views
8

我有一個TSQL腳本,它執行了很多數據庫結構調整,但是當某些事情失敗時讓它通過並不是很安全。在錯誤腳本中退出並回滾所有內容

來把事情說清楚:

  • 使用MS SQL 2005
  • 它不是一個存儲過程,只是一個腳本文件(.SQL)

我有什麼東西在下面訂單

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在錯誤發生後嘗試執行每個語句。 (我注意到當新表出現時,當我不期待他們,因爲它應該回滾)

回答

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

這基本上是我使用,但在以下形式: 如果@@錯誤<> 0或@@ TRANCOUNT = 0開始 \t \t如果@@ TRANCOUNT> 0回滾事務 \t \t集上 \t – 2010-06-10 07:55:48

+1

端設置'NOEXEC on' NOEXEC是巧妙的方法,必須記住。 – Blorgbeard 2010-06-10 10:51:53

+0

這是否適用於服務器級別的事務,如「DROP LOGIN」?我想更改登錄所屬的域,並從所有數據庫中刪除它,但需要重新創建,同時確保NOTHING被刪除,如果不是每個事務成功提交。這會導致錯誤映射的權限等,這將是一場災難。 – Chiramisu 2013-08-29 18:02:15

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 

希望這有助於...

+1

您不能在'TRY-CATCH'中使用'GO'語句:http://msdn.microsoft.com/en-us/library/ms179296.aspx – Blorgbeard 2010-05-27 07:14:44

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。因此,我可以很好地提交或回滾相應的交易。

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 
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 
相關問題