【问题标题】:Catch block isn't executed in stored procedureCatch 块不在存储过程中执行
【发布时间】:2019-03-19 13:25:34
【问题描述】:

我有一个应该从数据库中删除模式的存储过程。我从后端执行这个存储过程(使用 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 块。根据this topic 内部存储过程中的错误应该由外部catch 处理。所以我认为一切都以正确的方式编写......但不正确的行为仍然存在。

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

我正在使用 SQL Server。

【问题讨论】:

  • 在不触发 CATCH 块的情况下如何知道发生了错误?您能否提供一个其他人可以运行的脚本来重现该行为?
  • @TabAlleman,很难重现这个“错误”或我对“错误”的假设。手动尝试过 - 但没有运气。
  • 我相信你需要在调用的每个单独的 sp 中都有 catch 块。如果有意义的话,存储过程中的错误超出了调用 SQL 的范围。
  • 你能分享其他程序的代码吗?他们也有尝试/静噪块吗?你的 catch 问题是,如果你确实捕获了一个错误,那么你已经吞下了细节。调试起来会非常痛苦,因为您只是将状态更新为 3 并且不对错误信息做任何有用的操作。
  • 使用 try/catch 并不能替代使用事务来保证在发生错误时回滚所有数据库更新。似乎您需要更彻底地研究 Erland 关于错误处理的讨论。吃掉错误将使诊断任何问题变得不可能。

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


【解决方案1】:

如果您的存储过程遵循这种框架,那么您的错误应该被传递回上游到顶部调用者中的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 报告了它。

【讨论】:

  • 问题 - 是否可以在 try-catch 块 SPs xy 中进行包装并从内部存储过程中重新抛出错误?
  • 是的,我会添加一个示例
  • 请注意,您可以在 y 中抛出异常,但为什么要这样做呢?如果你沿着链返回,那么我们会处理错误,在一个过程中使用它,然后将它传递回链,直到我们回到“过程零”。如果我们改为使用RAISEEXCEPTION,那么我们将不会拥有完全相同的控制水平。例如,我还可以传回一个上下文OUTPUT 参数,以帮助我在我的模型中进行调试,事实上我可以拥有任意数量的参数(达到限制)。但是,如果我沿着异常路线走,那么我只能将一条信息传递回链
【解决方案2】:

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

它将向外部 SP 抛出错误,您将能够对其进行跟踪。

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

Throw documentation

Try-Catch documentation

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-09
    • 1970-01-01
    • 2013-11-06
    • 2010-09-15
    • 1970-01-01
    • 2014-12-13
    • 2019-07-15
    相关资源
    最近更新 更多