在TSQL上尝试和捕获 - 抓住没有抓住

时间:2013-02-26 15:07:48

标签: sql-server sql-server-2008 tsql try-catch

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

代码错误,但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表。

3 个答案:

答案 0 :(得分:7)

TSQL' TRY...CATCH没有发现错误。这个错误属于"编译/重新编译"在同一级别的执行中,CATCH块"未处理类型错误"。

来自MSDN

  

当CATCH块不处理以下类型的错误   它们与TRY ... CATCH结构处于相同的执行级别:

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

  •   
  • 语句级重新编译期间发生的错误,例如   作为编译后发生的对象名称解析错误,因为   延迟名称解析

  •   

...

  

您可以使用TRY ... CATCH来处理编译期间发生的错误   或通过执行错误生成来进行语句级重新编译   代码在TRY块中的单独批处理中。例如,你这样做   这可以通过将代码放在存储过程中或通过执行   使用sp_executesql的动态Transact-SQL语句。这允许   TRY ... CATCH在比执行更高的执行级别捕获错误   错误发生。例如,以下代码显示已存储   生成对象名称解析错误的过程。批次   包含TRY ... CATCH结构正在更高级别执行   比存储过程;和错误,发生在较低的   等级,被捕获。

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

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

如果你希望它们都存在于一个脚本中,你可以将其设为temporary stored procedure,但是在创建sprocs时需要处理编译错误。它不漂亮,但它会起作用:

-- 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;

答案 1 :(得分:0)

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

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

答案 2 :(得分:-1)

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