2013-04-12 20 views
8

Vì vậy, tôi nhận được thông báo lỗi sau từ SQL Server khi sp_SomeProc cố gắng thực thi câu lệnh sql không hợp lệ. Tôi gặp lỗi:Lỗi kích hoạt: 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ý

The current transaction cannot be committed and cannot support operations that write to the log file. 

Bất kỳ ý tưởng nào về những gì tôi đang làm sai? ("Tại sao bạn làm điều này" đây chỉ là một mẫu mà tôi tạo ra để bắt chước các vấn đề vì vậy hãy không, "này có ý nghĩa quan an ninh", vv ..)


Vì vậy bàn của tôi trông giống như:

CREATE TABLE tSOMETABLE 
( 
    RecID INT NOT NULL IDENTITY(1,1) 
    Val VARCHAR(20), 
CONSTRAINT [PK_tSOMETABLE] PRIMARY KEY CLUSTERED 
(
    RecID ASC 
) 
) 

Vì vậy, trong kích hoạt của tôi, tôi có:

CREATE TRIGGER [dbo].[TR_tSOMETABLE_INSERT]  
    ON [dbo].[tSOMETABLE] 
    FOR INSERT 
AS  
SET NOCOUNT ON 
BEGIN 
     BEGIN 
      SELECT * INTO #temp FROM INSERTED 

      WHILE EXISTS (SELECT 1 FROM #temp) 
      BEGIN 
       DECLARE @RecID INT  
       SELECT @RecID = RecID 
       FROM #temp t 
       EXEC dbo.sp_SomeProc @EventType = 'ON INSERT', @RecID = @RecID 
       DELETE #temp WHERE @RecID = RecID 
      END   
     END 
END 

Bây giờ mã của sp_SomeProc trông giống như:

CREATE PROC sp_SomeProc 
(
    @EventType VARCHAR(50), 
    @RecID INT, 
    @Debug BIT = 0 
) 
AS 
BEGIN 
    SET NOCOUNT ON 

    DECLARE @ProcTable TABLE 
    (
     RecID INT NOT NULL IDENTITY(1,1), 
     Cmd VARCHAR(MAX) 
    ) 

    INSERT INTO @ProcTable(Cmd) 
     SELECT 'EXEC sp_who' 
     UNION 
     SELECT 'EXEC sp_SomeStoredProcThatDoesntExist' 


    DECLARE @RecID INT 
    SELECT @RecID = MIN(RecID) FROM @ProcTable 
    WHILE @RecID IS NOT NULL 
    BEGIN 
     DECLARE @sql VARCHAR(MAX) 
     SELECT @sql = cmd FROM @ProcTable WHERE RecID = @RecID 
     IF @Debug = 1 
      PRINT @sql 
     ELSE 
      BEGIN 
       BEGIN TRY  
        EXEC(@sql) 
       END TRY 
       BEGIN CATCH 
        DECLARE @Msg VARCHAR(MAX), @ErrorNumber INT, @ErrorSeverity INT, @ErrorState int, @ErrorProcedure nvarchar(256), @ErrorLine int, @ErrorMessage nvarchar(MAX) 
        SELECT @Msg = 'Failed While Executing: ' + @sql 
        SELECT @ErrorNumber = ERROR_NUMBER(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorProcedure = ERROR_PROCEDURE(), @ErrorLine = ERROR_LINE(), @ErrorMessage = ERROR_MESSAGE() 
        -- DO SOME MORE STUFF HERE AND THEN ... 
        RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState) 
       END CATCH 
      END 
     SELECT @RecID = MIN(RecID) FROM @ProcTable WHERE RecID > @RecID 
    END 
END 

Vì vậy, để kiểm tra tôi cố gắng:

INSERT INTO tSOMETABLE(Val) 
SELECT 'Hello' 
+0

Quay lại các khái niệm cơ bản. Trình kích hoạt thực sự đang cố gắng làm gì? Tại sao bạn không thể làm điều đó như là một hoạt động dựa trên thiết lập bên trong kích hoạt bằng cách sử dụng 'chèn' thay vì hàng lặp bởi hàng đau đớn và thực hiện một thủ tục riêng biệt, phức tạp được lưu trữ cho mỗi hàng? –

+0

Không thể, trình kích hoạt cần thực thi một số procs được lưu trữ cho mỗi hàng đang được chèn dựa trên dữ liệu đang đi vào hàng. Nó cần phải thực hiện sql động và nó không thể được thực hiện như là một hoạt động thiết lập. Tôi không kiểm soát được procs được lưu trữ mà nó thực hiện cho mỗi hàng. Trong việc thực hiện đầy đủ có một bảng quyết định sql nào sẽ chạy cho mỗi hàng INSERTED (nhưng điều đó không liên quan cho câu hỏi này) – Denis

Trả lời

38

Lỗi này xảy ra khi bạn sử dụng một khối try/catch bên trong một giao dịch. Hãy xem xét một ví dụ tầm thường:

SET XACT_ABORT ON 

IF object_id('tempdb..#t') IS NOT NULL 
    DROP TABLE #t 
CREATE TABLE #t (i INT NOT NULL PRIMARY KEY) 

BEGIN TRAN 
    INSERT INTO #t (i) VALUES (1) 
    INSERT INTO #t (i) VALUES (2) 
    INSERT INTO #t (i) VALUES (3) 
    INSERT INTO #t (i) VALUES (1) -- dup key error, XACT_ABORT kills the batch 
    INSERT INTO #t (i) VALUES (4) 

COMMIT TRAN 
SELECT * FROM #t 

Khi chèn thứ tư gây ra lỗi, lô được chấm dứt và giao dịch sẽ quay trở lại. Không có bất ngờ cho đến nay.

Bây giờ chúng ta hãy cố gắng để xử lý lỗi đó với một khối try/catch:

SET XACT_ABORT ON 
IF object_id('tempdb..#t') IS NOT NULL 
    DROP TABLE #t 
CREATE TABLE #t (i INT NOT NULL PRIMARY KEY) 

BEGIN TRAN 
    INSERT INTO #t (i) VALUES (1) 
    INSERT INTO #t (i) VALUES (2) 
    BEGIN TRY 
     INSERT INTO #t (i) VALUES (3) 
     INSERT INTO #t (i) VALUES (1) -- dup key error 
    END TRY 
    BEGIN CATCH 
     SELECT ERROR_MESSAGE() 
    END CATCH 
    INSERT INTO #t (i) VALUES (4) 
    /* Error the Current Transaction cannot be committed and 
    cannot support operations that write to the log file. Roll back the transaction. */ 

COMMIT TRAN 
SELECT * FROM #t 

Chúng tôi bắt gặp những lỗi khóa trùng lặp, nhưng mặt khác, chúng tôi không khấm khá hơn. Lô hàng của chúng tôi vẫn bị chấm dứt và giao dịch của chúng tôi vẫn được khôi phục. Lý do thực sự rất đơn giản:

Khối TRY/CATCH không ảnh hưởng đến giao dịch.

Do có XACT_ABORT ON, thời điểm lỗi khóa trùng lặp xảy ra, giao dịch sẽ bị hủy. Nó được thực hiện cho. Nó đã bị thương nặng. Nó bị bắn xuyên qua trái tim ... và lỗi của nó là đáng trách. TRY/CATCH cho SQL Server ... một tên xấu. (xin lỗi, không thể cưỡng lại)

Nói cách khác, nó sẽ KHÔNG BAO GIỜ cam kết và sẽ ALWAYS được khôi phục. Tất cả khối TRY/CATCH có thể làm là phá vỡ sự sụp đổ của xác chết. Chúng tôi có thể sử dụng chức năng XACT_STATE() để xem giao dịch của chúng tôi có thể giao dịch hay không. Nếu không, tùy chọn duy nhất là quay lại giao dịch.

SET XACT_ABORT ON -- Try with it OFF as well. 
IF object_id('tempdb..#t') IS NOT NULL 
    DROP TABLE #t 
CREATE TABLE #t (i INT NOT NULL PRIMARY KEY) 

BEGIN TRAN 
    INSERT INTO #t (i) VALUES (1) 
    INSERT INTO #t (i) VALUES (2) 

    SAVE TRANSACTION Save1 
    BEGIN TRY 
     INSERT INTO #t (i) VALUES (3) 
     INSERT INTO #t (i) VALUES (1) -- dup key error 
    END TRY 
    BEGIN CATCH 
     SELECT ERROR_MESSAGE() 
     IF XACT_STATE() = -1 -- Transaction is doomed, Rollback everything. 
      ROLLBACK TRAN 
     IF XACT_STATE() = 1 --Transaction is commitable, we can rollback to a save point 
      ROLLBACK TRAN Save1 
    END CATCH 
    INSERT INTO #t (i) VALUES (4) 

IF @@TRANCOUNT > 0 
    COMMIT TRAN 
SELECT * FROM #t 

Trình kích hoạt luôn thực thi trong ngữ cảnh giao dịch, vì vậy nếu bạn có thể tránh sử dụng TRY/CATCH bên trong chúng, mọi thứ đơn giản hơn rất nhiều.

Để giải pháp cho vấn đề của bạn, một CLR được lưu trữ có thể kết nối lại với SQL Server trong một kết nối riêng biệt để thực thi SQL động. Bạn có được khả năng thực thi mã trong một giao dịch mới và logic xử lý lỗi dễ viết và dễ hiểu trong C#.

+6

+1 cho tham chiếu Bon Jovi;) Câu trả lời hữu ích nữa. Cảm ơn! – t0mm0d

+1

Điều gì sẽ xảy ra nếu tôi gặp lỗi này và tôi không tham gia giao dịch? Tôi đang sử dụng thử/bắt để thử và phân tích các ngày khác nhau. Excel sử dụng một "số sê-ri", nhưng một số khác sử dụng một ngày bình thường. Ngoài ra, nếu đó là sql động và một giao dịch có lỗi nếu bị cuốn vào giao dịch không? –

+1

Tôi nhận được lỗi này ngay cả khi tôi không sử dụng GIAO DỊCH, tôi đang kết nối với cá thể cơ sở dữ liệu khác nhau thông qua máy chủ được liên kết. – Muflix

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