Блок захвата не выполняется в хранимой процедуре - PullRequest
0 голосов
/ 19 марта 2019

У меня есть хранимая процедура, которая должна удалить схему из базы данных.Я выполняю эту хранимую процедуру из бэкэнда (используя C #).В результате я хочу получить логическое значение, если удаление прошло успешно или нет.

var result = await session.Sp<bool>(c => c.spDeleteSchemaTotally, new { schemaName})
                                         .SingleAsync()
                                         .ConfigureAwait(false);

if (result)
{
     tracer.Info("Deleted successfully");
}
else
{
     tracer.Warn("Schema was not deleted appropriately");
}

Хранимая процедура:

ALTER PROCEDURE [dbo].[spDeleteSchemaTotally]
    (@schemaName NVARCHAR(100))
AS
    SET FMTONLY OFF
BEGIN TRY
    UPDATE SchemaList
    SET [Status] = 2 /* DELETING */
    WHERE SchemaName = @schemaName

    EXEC [dbo].[spDeleteSchema] @schemaName

    EXEC [dbo].[spDeleteSchemaOwner] @schemaName

    EXEC [dbo].[spDeleteSchemaUserRoles] @schemaName

    UPDATE SchemaList
    SET [Status] = 1 /* DELETED */,
        SchemaName = SchemaName + '|deleted|' + FORMAT(GETUTCDATE(), 'MM-d-yyyy-hh:mm')
    WHERE SchemaName = @schemaName

    SELECT CAST(1 AS BIT);
END TRY
BEGIN CATCH
    UPDATE SchemaList
    SET [Status] = 3 /* ERRORED */
    WHERE SchemaName = @schemaName

    SELECT CAST(0 AS BIT);
END CATCH 

У меня есть таблица, в которой я храню список всехсхемы.Для каждой схемы у меня есть статус.Каждая схема имеет несколько собственных таблиц, правил и т. Д.

Но по некоторым причинам я заметил, что возникает некоторая ошибка (иногда), и схема остается со статусом 2 (удаление).Что за ошибка - я не знаю, потому что не могу поймать этот момент.

Логично подумать, что внутри хранимых процедур происходит какая-то ошибка, но для этого у меня есть блок catch.Согласно этой теме ошибка внутри некоторой внутренней хранимой процедуры должна обрабатываться внешним перехватчиком.Поэтому я думаю, что все написано правильно ... но не правильное поведение все еще существует.

Может кто-нибудь указать мне правильный способ обработки ошибок в вышеописанной хранимой процедуре?

Я использую SQL Server.

Ответы [ 2 ]

0 голосов
/ 19 марта 2019

Если ваши хранимые процедуры следуют такого рода инфраструктуре, то ваша ошибка должна быть передана обратно в блок CATCH в верхней вызывающей стороне:

CREATE PROCEDURE y 
AS
BEGIN
    SELECT 1 / 0;
END;
GO
CREATE PROCEDURE x
AS
BEGIN
    SET NOCOUNT ON;
    BEGIN TRY
        EXEC y;
        SELECT 0 AS result;
    END TRY
    BEGIN CATCH
        PRINT error_message();
        SELECT 1 AS result;
    END CATCH;
END;
GO
EXEC x;

Если я выполню это, тогда процедура y сгенерируетошибка «Ошибка деления на ноль», которая затем передается обратно в процедуру x, где она обрабатывается и сообщается.

Если в процедуре y есть блок TRY-CATCH, то это будет использовать ошибку и ничегобудет отправлен обратно в процедуру x.

Кроме того, вы можете использовать RETURN 0 или RETURN 1, чтобы возвратить статус успеха / неудачи вашей хранимой процедуры, вместо использования запроса SELECT, чтобы сделать это,это просто лучшая практика.Если у вас есть дополнительная информация для передачи, например, сообщение об ошибке, вы можете использовать для этого параметр OUTPUT.

Например:

CREATE PROCEDURE y 
AS
BEGIN
    SELECT 1 / 0;
END;
GO
CREATE PROCEDURE x (
    @message VARCHAR(512) OUTPUT)
AS
BEGIN
    SET NOCOUNT ON;
    BEGIN TRY
        EXEC y;
        RETURN 0;
    END TRY
    BEGIN CATCH
        SELECT @message = error_message();
        RETURN 1;
    END CATCH;
END;
GO
DECLARE @message VARCHAR(512);
DECLARE @ret INT;
EXEC @ret = x @message OUTPUT;
SELECT @ret, @message;

Или вотПример повторного прохождения внутреннего исключения снова:

CREATE PROCEDURE y (
    @message VARCHAR(512) OUTPUT)
AS
BEGIN
    BEGIN TRY
        SELECT 1 / 0;
        RETURN 0;
    END TRY
    BEGIN CATCH
        SELECT @message = 'Inner ' + ERROR_MESSAGE();
        RETURN 1;
    END CATCH;
END;
GO
CREATE PROCEDURE x (
    @message VARCHAR(512) OUTPUT)
AS
BEGIN
    SET NOCOUNT ON;
    BEGIN TRY
        DECLARE @ret INT;
        EXEC @ret = y @message OUTPUT;
        RETURN @ret;
    END TRY
    BEGIN CATCH
        SELECT @message = 'Outer ' + ERROR_MESSAGE();
        RETURN 1;
    END CATCH;
END;
GO
DECLARE @message VARCHAR(512);
DECLARE @ret INT;
EXEC @ret = x @message OUTPUT;
SELECT @ret, @message;

При запуске вы получите статус возврата 1 (исключение) и сообщение об ошибке: «Обнаружена ошибка внутреннего деления на ноль». ".Итак, мы знаем, что эта ошибка на самом деле была вызвана процедурой y, хотя процедура x сообщила об этом.

0 голосов
/ 19 марта 2019

Если каждая из внутренних хранимых процедур имеет конструкцию Try-Catch, вам нужно добавить команду THROW в блоки Catch каждого внутреннего SP.

Он выдаст ошибку внешнему SP, и вы сможете отследить ее.

Идея состоит в том, что если ошибка возникает во внутреннем SP с блоком Try-Catch, внешний SP не отказывает и не знает, что произошла ошибка.

Бросок документации

Try-Catch документация

...