SQL Как;Try-Catch информация об ошибке в таблицу журнала ошибок, для хранимого процесса, выполненного в транзакции? - PullRequest
0 голосов
/ 09 января 2012

В моих хранимых процессах я использую Try-Catch и вызываю процедуру обработки ошибок в блоке Catch, которая записывает подробности об ошибках в таблицу ErrorLog, а затем перебрасывает ошибку.

В своем коде C # я выполняю свои хранимые процедуры, используя:

using(TransactionScope scope = new TransactionScope()) {
    // execute stored procs
    scope.Complete();
}

Проблема, с которой я сталкиваюсь, если транзакция прервана (scope.Complete никогда не вызывается), мой обработанный обработчик ошибок proc действительно выдает ошибку sql, но не может записать ошибку в таблицу ErrorLog, потому что она находится в контекст транзакции; Есть ли способ обойти это? Я уже знаю, что данные не могут быть вставлены, когда транзакция находится в незафиксированном состоянии, так как мне выйти из транзакции и по-прежнему регистрировать ошибку?

Код TSQL:

BEGIN PROCEDURE [dbo].[usp_UpsertSomething]
    @SomethingID BIGINT
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRY
        -- do something
    END TRY
    BEGIN CATCH
        EXEC dbo.cp_RethrowError
        RETURN -1
    END CATCH;
END

CREATE PROCEDURE [dbo].[usp_RethrowError]
    @ErrorLogID [INT] = 0 OUTPUT -- Contains the ErrorLogID of the row inserted
                                 -- by cp_RethrowError in the ErrorLog table.
AS
BEGIN
    -- Return if there is no error information to retrieve.
    IF ERROR_NUMBER() IS NULL
        RETURN;

    DECLARE 
        @ErrorMessage           VARCHAR(4000),
        @FormattedErrorMessage  VARCHAR(4000),
        @ErrorNumber            INT,
        @ErrorSeverity          INT,
        @ErrorState             INT,
        @ErrorLine              INT,
        @ErrorProcedure         VARCHAR(200);

    -- Assign variables to error-handling functions that 
    -- capture information for RAISERROR.
    SELECT 
        @ErrorNumber = ERROR_NUMBER(),
        @ErrorMessage = ERROR_MESSAGE(),
        @ErrorSeverity = ERROR_SEVERITY(),
        @ErrorState = ERROR_STATE(),
        @ErrorLine = ERROR_LINE(),
        @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');

    -- Build the message string that will contain original
    -- error information.
    SELECT @FormattedErrorMessage = 
        'ErrorLogID %d, Error %d, Level %d, State %d, Procedure %s, ' + 
            'Line %d, Message: '+ @ErrorMessage;

    BEGIN TRY
        -- Data insertion/modification is not allowed when 
        -- a transaction is in an uncommittable state.
        IF XACT_STATE() = -1 BEGIN
            SET @ErrorLogID = 0;
        END
        ELSE BEGIN
            INSERT [dbo].[ErrorLog] 
            (
                ErrorNumber, 
                ErrorSeverity, 
                ErrorState, 
                ErrorProcedure, 
                ErrorLine, 
                ErrorMessage
            ) 
            VALUES 
            (
                @ErrorNumber,
                @ErrorSeverity,
                @ErrorState,
                @ErrorProcedure,
                @ErrorLine,
                @ErrorMessage
            );

            -- Pass back the ErrorLogID of the row inserted
            SELECT @ErrorLogID = @@IDENTITY;
        END
    END TRY
    BEGIN CATCH
        PRINT 'An error occurred in stored procedure cp_RethrowError.';
    END CATCH

    -- Raise an error: msg_str parameter of RAISERROR will contain
    -- the original error information.
    RAISERROR 
    (
        @FormattedErrorMessage, 
        @ErrorSeverity, 
        1,
        @ErrorLogID,     -- parameter: ErrorLogID in ErrorLog table.
        @ErrorNumber,    -- parameter: original error number.
        @ErrorSeverity,  -- parameter: original error severity.
        @ErrorState,     -- parameter: original error state.
        @ErrorProcedure, -- parameter: original error procedure name.
        @ErrorLine       -- parameter: original error line number.
    );
END

Ответы [ 3 ]

0 голосов
/ 02 июня 2014

Вы также можете сначала поместить записи журнала ошибок в переменную таблицы (которая должна иметь ту же структуру, что и таблица реальных журналов). Откат не очищает переменные таблицы. В конце вам нужно только вставить содержимое табличной переменной в реальную таблицу журнала.

Я не пробовал, но это может сработать. В SQL Server по-прежнему отсутствует такая функция, как автономная транзакция (у Oracle она уже есть). Все, что вы делаете в автономной транзакции, не зависит от основной транзакции. Это именно то, что вам нужно в случае регистрации ошибок: особенно, когда транзакция откатывается, вы все равно хотите знать, что произошло.

0 голосов
/ 15 марта 2017

Это проблема секвенирования.Проверьте количество транзакций в вашем обработчике ошибок.Шаги: 1. Сначала получите исходную информацию об ошибке в @variables или @table.2. Проверьте количество транзакций и, если оно больше 0, откатите его.3. Вставьте информацию об ошибке, которую вы только что собрали на шаге 1. Готово.

0 голосов
/ 10 января 2012

Поскольку транзакции являются сделкой "все или ничего", единственный способ выполнить то, что я хочу, - это вызвать ROLLBACK TRANSACTION внутри блока Catch, прежде чем вызывать мой вызов обработки ошибок.

Мой журнал ошибок заканчивается ошибкой, которую я хочу зарегистрировать, но в моем блоке перехвата .Net я получаю жалобу от SQL: «Число транзакций после EXECUTE указывает на отсутствие оператора COMMIT или ROLLBACK TRANSACTION. Предыдущий счет = 1, текущий счетчик = 0. "

Я могу жить с этим!

...