捕获块未在存储过程中执行

时间:2019-03-19 13:25:35

标签: c# sql sql-server stored-procedures error-handling

我有一个存储过程,应该从数据库中删除模式。我从后端(使用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块。根据{{​​3}},某些内部存储过程内部的错误应由外部catch处理。因此,我认为以正确的方式编写的所有内容……但仍然存在不正确的行为。

有人可以指出我上述存储过程中处理错误的正确方法吗?

我正在使用SQL Server。

2 个答案:

答案 0 :(得分:0)

如果每个内部存储过程确实具有Try-Catch构造,则需要向每个内部SP的Catch块中添加THROW命令。

它将向外部SP引发错误,您将可以对其进行跟踪。

这个想法是,如果内部带有Try-Catch块的SP中发生错误,则外部SP不会失败并且也不知道有错误。

Throw documentation

Try-Catch documentation

答案 1 :(得分:0)

如果您的存储过程遵循这种框架,则应将您的错误传递回上游,返回到顶级调用者中的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 0RETURN 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报告了该错误。