2010-05-26 18 views
8

Tôi có một tập lệnh TSQL thực hiện rất nhiều điều chỉnh cấu trúc cơ sở dữ liệu nhưng không thực sự an toàn khi để nó đi qua khi có lỗi.Thoát và khôi phục mọi thứ trong tập lệnh về lỗi

để làm cho mọi việc rõ ràng:

  • sử dụng MS SQL 2005
  • nó không phải là một thủ tục lưu trữ, chỉ cần một tập tin script (sql)

những gì tôi có là một cái gì đó trong những điều sau đây đặt hàng

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 

chỉ để minh họa những gì tôi đang làm việc ... không thể đi vào chi tiết cụ thể bây giờ, vấn đề ...

Khi tôi giới thiệu một lỗi (để kiểm tra xem mọi thứ có được khôi phục không), tôi nhận được một tuyên bố rằng giao dịch ROLLBACK không thể tìm thấy một GIAO DỊCH BEGIN tương ứng. Điều này khiến tôi tin rằng một cái gì đó khi REALLY sai và giao dịch đã bị giết. những gì tôi cũng nhận thấy là kịch bản đã không hoàn toàn thoát khỏi lỗi và do đó DID cố gắng thực thi mọi câu lệnh sau khi lỗi xảy ra. (Tôi nhận thấy điều này khi các bảng mới xuất hiện khi tôi không mong đợi chúng vì nó sẽ được khôi phục)

Trả lời

7

Khi lỗi xảy ra, giao dịch được tự động cuộn lại và lô hiện tại bị hủy bỏ. Tuy nhiên,

Thực hiện tiếp tục vào lô tiếp theo. Vì vậy, tất cả các công cụ trong các lô sau khi lỗi được thực hiện. Và sau đó khi bạn kiểm tra lỗi sau, bạn cố gắng khôi phục giao dịch đã được cuộn lại.

Ngoài ra, để ngăn chặn toàn bộ kịch bản, không chỉ là hàng loạt hiện tại, bạn nên sử dụng:

raiserror('Error description here', 20, -1) with log 

Xem my answer here biết chi tiết về một đó.

Vì vậy, bạn cần phải kiểm tra cho @error sau mỗi đợt, tôi nghĩ rằng một cái gì đó như thế này nên làm việc:

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

này về cơ bản là những gì tôi được sử dụng nhưng theo hình thức sau: nếu @@ lỗi <> 0 hoặc @@ trancount = 0 bắt đầu \t \t nếu @@ trancount> 0 rollback giao dịch \t \t bộ noexec trên \t cuối –

+1

'thiết on' noexec là một thủ thuật gọn gàng, phải nhớ rằng. – Blorgbeard

+0

Tính năng này có hoạt động với giao dịch cấp máy chủ như 'DROP LOGIN' không? Tôi muốn thay đổi tên miền đăng nhập thuộc về và đang thả nó từ tất cả các DB nhưng cần phải tạo lại trong khi đảm bảo NOTHING bị giảm nếu không MỌI giao dịch cam kết thành công. Điều này sẽ dẫn đến các điều khoản bị ánh xạ sai, vv và sẽ là một thảm họa. – Chiramisu

0

Bạn có thể thử một cái gì đó như thế này ... Nếu bạn đang sử dụng khối Try ... Các lỗi level 16, (hay nhất của lỗi ứng dụng), ngay lập tức chuyển sự kiểm soát để các khối catch mà không thực hiện bất kỳ báo cáo hơn nữa trong khối 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 

Hope this helps ...

+1

Bạn không thể có câu lệnh 'GO' bên trong' TRY-CATCH': http://msdn.microsoft.com/en-us/library/ms179296.aspx – Blorgbeard

3

Tôi không sử dụng giải pháp nâng cao, vì nó không thành công vì tôi không có quyền quản trị. Tôi mở rộng noexec on/off giải pháp với xử lý các giao dịch như sau:

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 

Rõ ràng trình biên dịch "hiểu" biến @finished trong IF, thậm chí nếu có một lỗi và thực hiện được vô hiệu hóa. Tuy nhiên, giá trị được đặt thành 1 chỉ khi thực thi không bị vô hiệu hóa. Do đó tôi có thể độc đáo cam kết hoặc khôi phục giao dịch cho phù hợp.

4

Thử sử dụng RETURN. điều này sẽ thoát khỏi tập lệnh hoặc thủ tục ngay lập tức và sẽ không thực hiện bất kỳ câu lệnh nào sau đây.Bạn có thể sử dụng kết hợp với BEGIN, ROLLBACK và những lệnh COMMIT TRANSACTION để hoàn tác bất kỳ thiệt hại dữ liệu:

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 
Các vấn đề liên quan