2011-09-20 32 views
26

Tôi gặp sự cố tương tự với The current transaction cannot be committed and cannot support operations that write to the log file, nhưng tôi có một câu hỏi tiếp theo.Lỗi giao dịch SQL: Giao dịch hiện tại không thể được cam kết và không thể hỗ trợ các thao tác ghi vào tệp nhật ký

Câu trả lời có tham chiếu Using TRY...CATCH in Transact-SQL, mà tôi sẽ trở lại trong một giây ...

Mã của tôi (di truyền, tất nhiên) có dạng đơn giản:

SET NOCOUNT ON 
SET XACT_ABORT ON 

CREATE TABLE #tmp 

SET @transaction = 'insert_backtest_results' 
BEGIN TRANSACTION @transaction 

BEGIN TRY 

    --do some bulk insert stuff into #tmp 

END TRY 

BEGIN CATCH 
    ROLLBACK TRANSACTION @transaction 
    SET @errorMessage = 'bulk insert error importing results for backtest ' 
     + CAST(@backtest_id as VARCHAR) + 
     '; check backtestfiles$ directory for error files ' + 
     ' error_number: ' + CAST(ERROR_NUMBER() AS VARCHAR) + 
     ' error_message: ' + CAST(ERROR_MESSAGE() AS VARCHAR(200)) + 
     ' error_severity: ' + CAST(ERROR_SEVERITY() AS VARCHAR) + 
     ' error_state ' + CAST(ERROR_STATE() AS VARCHAR) + 
     ' error_line: ' + CAST(ERROR_LINE() AS VARCHAR) 
    RAISERROR(@errorMessage, 16, 1) 
    RETURN -666 
END CATCH 

BEGIN TRY 

    EXEC usp_other_stuff_1 @whatever 

    EXEC usp_other_stuff_2 @whatever 

    -- a LOT of "normal" logic here... inserts, updates, etc... 

END TRY 

BEGIN CATCH 

    ROLLBACK TRANSACTION @transaction 
    SET @errorMessage = 'error importing results for backtest ' 
     + CAST(@backtest_id as VARCHAR) + 
     ' error_number: ' + CAST(ERROR_NUMBER() AS VARCHAR) + 
     ' error_message: ' + CAST(ERROR_MESSAGE() AS VARCHAR(200)) + 
     ' error_severity: ' + CAST(ERROR_SEVERITY() AS VARCHAR) + 
     ' error_state ' + CAST(ERROR_STATE() AS VARCHAR) + 
     ' error_line: ' + CAST(ERROR_LINE() AS VARCHAR) 
    RAISERROR(@errorMessage, 16, 1) 
    RETURN -777 

END CATCH 

RETURN 0 

Tôi nghĩ tôi có đủ thông tin để chỉ chơi với nó và tìm ra bản thân mình ... tiếc là tái tạo lỗi là chứng minh damn gần không thể. Vì vậy, tôi hy vọng rằng yêu cầu ở đây sẽ giúp làm rõ sự hiểu biết của tôi về vấn đề và giải pháp.

thủ tục lưu trữ này là, không liên tục, ném lỗi như thế này một:

error importing results for backtest 9649 error_number: 3930 error_message: The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction. error_severity: 16 error_state 1 error_line: 217

Vì vậy, rõ ràng là lỗi xuất phát từ khối catch 2

Dựa trên những gì tôi đã đọc trong Using TRY...CATCH in Transact-SQL, tôi nghĩ rằng những gì đang xảy ra là khi trường hợp ngoại lệ bị ném, việc sử dụng XACT_ABORT sẽ khiến giao dịch bị "chấm dứt và quay lại" ... và sau đó dòng đầu tiên của BEGIN CATCH đang cố gắng quay trở lại một cách mù quáng.

Tôi không biết tại sao nhà phát triển ban đầu bật XACT_ABORT, vì vậy tôi nghĩ giải pháp tốt hơn (thay vì xóa) sẽ sử dụng XACT_STATE() để chỉ quay lại nếu có giao dịch (<>0). Điều đó có hợp lý không? Tui bỏ lỡ điều gì vậy?

Ngoài ra, việc đề cập đến việc đăng nhập vào thông báo lỗi khiến tôi tự hỏi: Có vấn đề gì khác, có khả năng với cấu hình không? Việc chúng tôi sử dụng RAISEERROR() trong trường hợp này có góp phần vào vấn đề này không? Điều đó có được đăng nhập hay không, trong một số trường hợp không thể đăng nhập, vì thông báo lỗi ám chỉ đến?

Trả lời

27

Bạn luôn cần phải kiểm tra XACT_STATE(), không liên quan đến cài đặt XACT_ABORT. Tôi có một ví dụ về một mẫu trong các stored procedure mà cần phải xử lý các giao dịch trong bối cảnh try/catch tại Exception handling and nested transactions:

create procedure [usp_my_procedure_name] 
as 
begin 
    set nocount on; 
    declare @trancount int; 
    set @trancount = @@trancount; 
    begin try 
     if @trancount = 0 
      begin transaction 
     else 
      save transaction usp_my_procedure_name; 

     -- Do the actual work here 

lbexit: 
     if @trancount = 0 
      commit; 
    end try 
    begin catch 
     declare @error int, @message varchar(4000), @xstate int; 
     select @error = ERROR_NUMBER(), 
       @message = ERROR_MESSAGE(), 
       @xstate = XACT_STATE(); 
     if @xstate = -1 
      rollback; 
     if @xstate = 1 and @trancount = 0 
      rollback 
     if @xstate = 1 and @trancount > 0 
      rollback transaction usp_my_procedure_name; 

     raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ; 
    end catch 
end 
+0

Mẫu của bạn giả định các giao dịch bên trong khối thử; chúng tôi có nhiều khối thử bên trong 1 giao dịch. –

+0

@Adam: Đó là về cách bạn xử lý 'XACT_STATE' và các giao dịch trong khối CATCH. Bạn có thể có nhiều khối thử trong một giao dịch bằng cách sử dụng mẫu rất này. Ý tưởng là hiểu cách giao dịch và khối catch tương tác, và như một phần thưởng bạn cũng nhận được việc xử lý các giao dịch lồng nhau và các điểm lưu trữ, rất hữu ích trong xử lý hàng loạt, vì nó cho khả năng tiếp tục phần còn lại của lô ngay cả khi mục nhập không thành công. –

+0

Tôi đã đi trước và gói câu lệnh rollback trong một 'if XACT_STATE() <> 0', nhưng chỉ có thời gian sẽ cho biết nếu nó giải quyết nó cho chúng ta. Đoán tôi sẽ tiếp tục và chấp nhận câu trả lời của bạn ngay bây giờ. –

12

Có một vài hiểu lầm trong các cuộc thảo luận ở trên.

Trước tiên, bạn luôn có thể ROLLBACK giao dịch ... bất kể trạng thái giao dịch là gì. Vì vậy, bạn chỉ phải kiểm tra XACT_STATE trước một COMMIT, chứ không phải trước khi quay trở lại.

Theo như lỗi trong mã, bạn sẽ muốn đặt giao dịch bên trong TRY. Sau đó, trong CATCH của bạn, điều đầu tiên bạn nên làm là:

IF @@TRANCOUNT > 0 
     ROLLBACK TRANSACTION @transaction 

Sau đó, sau tuyên bố ở trên, bạn có thể gửi email hoặc bất cứ điều gì cần thiết. (FYI: Nếu bạn gửi email TRƯỚC KHI rollback, sau đó bạn chắc chắn sẽ nhận được lỗi "không thể ... ghi vào tệp nhật ký").

Vấn đề này là từ năm ngoái, vì vậy tôi hy vọng bạn đã giải quyết vấn đề này bây giờ :-) Remus chỉ cho bạn đi đúng hướng.

Theo quy tắc chung ... TRY sẽ ngay lập tức chuyển đến CATCH khi có lỗi. Sau đó, khi bạn đang ở trong CATCH, bạn có thể sử dụng XACT_STATE để quyết định xem bạn có thể cam kết hay không. Nhưng nếu bạn luôn muốn ROLLBACK trong phần đánh bắt, thì bạn không cần phải kiểm tra trạng thái.

Các vấn đề liên quan