Ошибка триггера: текущая транзакция не может быть зафиксирована и не поддерживает операции записи в файл журнала

поэтому я получаю следующее сообщение об ошибке от SQL Server, когда sp_SomeProc пытается выполнить недопустимый оператор sql. Я получаю сообщение об ошибке:

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

любые идеи о том, что я делаю неправильно? (это просто образец, который я создал, чтобы имитировать проблему, поэтому, пожалуйста, не "почему вы это делаете?", "это имеет последствия для безопасности" и т. д..)


так мой стол выглядит так:

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

так что в моем триггере я есть:

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

теперь код sp_SomeProc выглядит так:

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

поэтому, чтобы проверить, я пытаюсь:

INSERT INTO tSOMETABLE(Val)
SELECT 'Hello'

1 ответов


эта ошибка возникает при использовании блока 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)
    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

когда четвертая вставка вызывает ошибку, пакет завершается и транзакция откатывается. Пока никаких сюрпризов.

Теперь давайте попытаемся обработать эту ошибку с помощью блока 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

мы поймали дубликат ключа ошибки, но в противном случае, мы не лучше. Наша партия по-прежнему прекращается, и наша транзакция все еще откатывается назад. Причина на самом деле очень просто:

блоки TRY/CATCH не влияют на транзакции.

из-за того, что XACT_ABORT включен, в момент возникновения ошибки дубликата ключа транзакция обречена. Все кончено. Он смертельно ранен. Стреляли в сердце...и виновата ошибка. TRY / CATCH дает SQL Server...плохая репутация. (извините, не удержался)

другими словами, это будет никогда commit и будет всегда откат. Все, что может сделать блок TRY/CATCH, это сломать падение трупа. Мы можем использовать функция xact_state()