4

我有一个似乎没有正确记录其错误的存储过程。

代码出错,但 catch 块似乎没有生效。

try 块相当长 - 但错误部分很简单,并且在最后,所以我已经精确了。

BEGIN TRY 
insert into tbl_X
select * from #temp_tbl_Y

RETURN 1
END TRY

BEGIN CATCH
    Insert Into ExtractsErrorLog
    SELECT 
    getdate() as ErrorDate 
    ,object_name(@@procid) as ProcedureName
    ,ERROR_NUMBER() as ErrorNumber
    ,ERROR_LINE() as ErrorLine
    ,ERROR_MESSAGE() as ErrorMessage
    ;
DECLARE @errormessage as varchar(max);
DECLARE @errorseverity as int;
DECLARE @errorstate as int;

set @errormessage = ERROR_MESSAGE();
set @errorseverity = ERROR_SEVERITY();
set @errorstate = ERROR_STATE();

 RAISERROR (@errormessage,
            @errorseverity,
            @errorstate
               );


END CATCH;

proc 失败的错误是我们的老朋友“列名或提供的值的数量与表定义不匹配”。我已经修复了这个错误——这是一个愚蠢的懒惰错误——但我很困惑为什么我的错误记录过程似乎没有工作——没有行被插入到我的 ExtractsErrorLog 表中。

4

3 回答 3

11

TSQLTRY...CATCH没有捕捉到那个错误。此错误属于“编译/重新编译”类型的错误,这些错误未由CATCH“在同一执行级别内”的块处理。

来自MSDN

以下类型的错误发生在与 TRY...CATCH 构造相同的执行级别时,不会由 CATCH 块处理:

  • 编译错误,例如语法错误,阻止批处理运行。

  • 语句级重新编译过程中出现的错误,例如由于延迟名称解析而在编译后出现的对象名称解析错误

...

您可以使用 TRY...CATCH 来处理编译或语句级重新编译期间发生的错误,方法是在 TRY 块内的单独批处理中执行生成错误的代码。例如,您可以通过将代码放在存储过程中或使用 sp_executesql 执行动态 Transact-SQL 语句来执行此操作。这允许 TRY…CATCH 在比错误发生更高的执行级别捕获错误。例如,以下代码显示了一个生成对象名称解析错误的存储过程。包含 TRY...CATCH 构造的批处理在比存储过程更高的级别上执行;并且捕获发生在较低级别的错误。

我在一个脚本中遇到了类似的问题,它在一个内部创建了一个事务TRY...CATCHROLLBACK如果它失败了。事务中的一条语句引发了同样的错误,并导致事务永远不会关闭,因为CATCH从未输入过。

正如 MSDN 文章中提到的,一种替代方法是从您的INSERT语句中创建一个存储过程,然后在您的 try/catch 中调用它。如果存储过程错误,您将在尝试创建它时发现编译错误。如果表定义稍后更改以使存储过程无效,那么TRY...CATCH它将为您捕获异常。

如果您希望它全部存在于一个脚本中,您可以将其设为临时存储过程,但是您需要在创建存储过程时处理编译错误。它不漂亮,但它会起作用:

-- Creating error sproc to re-use code
CREATE PROCEDURE #HandleError AS
    Insert Into ExtractsErrorLog
    SELECT  GETDATE() as ErrorDate 
            ,object_name(@@procid) as ProcedureName
            ,ERROR_NUMBER() as ErrorNumber
            ,ERROR_LINE() as ErrorLine
            ,ERROR_MESSAGE() as ErrorMessage;

    DECLARE @errormessage as varchar(max);
    DECLARE @errorseverity as int;
    DECLARE @errorstate as int;

    set @errormessage = ERROR_MESSAGE();
    set @errorseverity = ERROR_SEVERITY();
    set @errorstate = ERROR_STATE();

    RAISERROR ( @errormessage,
                @errorseverity,
                @errorstate);
GO

-- Create a stored procedure of our INSERT and catch any compilation errors
CREATE PROCEDURE #TEST AS
    insert into tbl_X
    select * from #temp_tbl_Y
GO
IF (@@ERROR <> 0) BEGIN
    exec #HandleError
    -- If there was an error creating the sprocs, don't continue to the next batch
    RETURN
END

-- If compilation succeeded, then run the sproc
BEGIN TRY 
    exec #TEST
    RETURN
END TRY
BEGIN CATCH
    exec #HandleError
END CATCH;
于 2014-10-16T16:20:20.643 回答
0

我在我的日志记录的 INSERT 语句之前在我的 CATCH 块中使用了 THROW - 并遇到了与您相同的问题。一旦我在记录 INSERT 语句之后移动了 THROW,它就起作用了。看起来 THROW 可能会终止会话。

您没有在代码示例中使用 THROW,但认为这可能对其他人有所帮助。

于 2019-07-19T21:05:49.067 回答
-1

这是您的 RETURN:“从查询或过程中无条件退出。RETURN 是立即且完整的,可以在任何时候用于退出过程、批处理或语句块。”

于 2013-02-26T17:50:30.827 回答