【问题标题】:What is wrong with my Try Catch in T-SQL?我在 T-SQL 中的 Try Catch 有什么问题?
【发布时间】:2010-12-29 17:33:12
【问题描述】:

我使用的是 SQL Server 2008,当我在 Management Studio 中运行此语句时,Catch 块中的 Select 语句按预期执行

BEGIN TRY
 INSERT INTO IDontExist(ProductID)
 VALUES(1)
END TRY
BEGIN CATCH
SELECT 'There was an error! ' + ERROR_MESSAGE()
END CATCH

但是,当我运行此语句时,Catch 块中的语句永远不会执行,而是仅在结果选项卡中显示错误

BEGIN TRY
  Select * from IDontExist
END TRY
BEGIN CATCH
  SELECT 'There was an error! ' + ERROR_MESSAGE()
END CATCH

它们都返回相同的错误号“208”“无效的对象名称:IDontExist”,那么为什么一个会被处理而另一个不会呢?

【问题讨论】:

  • 请对我们的回答有任何反馈?我很好奇你是怎么过的……
  • 迟来的问题:如果你运行 DBCC FREEPROCCACHE 来强制重新编译会怎样
  • 我找到的最佳答案是使用“SET XACT_ABORT ON”。如果打开它,它将捕获这些。 stackoverflow.com/questions/1150032/…

标签: sql-server


【解决方案1】:

我根本没有遇到 CATCH 块。

那是因为代码不会编译,因为对象不存在,没有生成计划,所以没有运行到 CATCH 块。

你可以永远点击这个 catch 块,所以你的测试/示例有问题。您可以在不同的范围内点击外部 catch 块(例如嵌套存储过程)

编辑:我使用的是 SQL Server 2005 SP3

这取决于何时应用延迟名称解析,与语句级重新编译有关。

  • 在我的例子中,整个批处理两次都失败,并且没有发生语句级重新编译,因此没有延迟名称解析

  • 在 OP 的情况下,批处理编译并运行,但在运行代码中出现语句级重新编译/延迟名称解析错误

我要去寻找一些关于为什么它不同的参考资料,因为 BOL 没有说太多,Erland Sommarskog 也没有说

【讨论】:

  • 我正在使用 SQL-2008,在 SSMS 中我看到执行了 catch 块:出现错误! Ungültiger Objektname 'IDontExist'。
  • @bernd_k:我使用的是 SQL Server 2005。行为发生了变化
  • 我正在使用 SQL Server 2008 并获得与您相同的行为。 @@VERSION = Microsoft SQL Server 2008 (SP2) - 10.0.4000.0 (X64) Sep 16 2010 19:43:16 Copyright (c) 1988-2008 Microsoft Corporation Developer Edition (64-bit) on Windows NT 6.1 <X64> (Build 7600: )
  • 在 SQL 2008 Enterprise 上测试。查询未编译。对象名称无效。
  • 如果我创建表,缓存计划,然后删除表,我现在可以重现此行为。
【解决方案2】:

这在过去也曾困扰过我。

并非所有在 TRY 块语句中生成的错误都会传递到 CATCH 块中。任何严重性为 10 或更低的错误都被视为警告,不会导致控制流向 CATCH 块。此外,任何中断数据库连接的错误都不会导致到达 CATCH 块。可能还有其他情况。

【讨论】:

  • 这与 OP 的案件有什么关系?错误 208 的严重性为 16,并且不会终止连接。
  • ..是的,这就是为什么有时将错误存储在表 @var 中是一个很好的解决方案,这应该使错误值在 CATCH 块中可用
【解决方案3】:

直接来自http://msdn.microsoft.com/en-us/library/ms175976.aspx

USE AdventureWorks2008R2;
GO

BEGIN TRY
    -- Table does not exist; object name resolution
    -- error not caught.
    SELECT * FROM NonexistentTable;
END TRY
BEGIN CATCH
    SELECT 
        ERROR_NUMBER() AS ErrorNumber
        ,ERROR_MESSAGE() AS ErrorMessage;
END CATCH

错误未被捕获,控制从 TRY...CATCH 结构传递到下一个更高级别。

在存储过程中运行 SELECT 语句将导致错误发生在低于 TRY 块的级别。错误将由 TRY...CATCH 构造处理。

【讨论】:

    【解决方案4】:

    如果您之前有一个表 IDontExist 并为其编译了仍在缓存中的计划然后删除该表,则会发生此行为。

    如果您运行单个语句两次,即使该表不存在,也会发生这种情况。第一次运行会引发未捕获的错误。第二次运行(缓存第一个计划后)成功。

    /*Clear Cache*/
    DBCC FREEPROCCACHE
    
    GO
    
    BEGIN TRY
     INSERT INTO IDontExist(ProductID)
     VALUES(1)
    END TRY
    BEGIN CATCH
    SELECT 'There was an error! ' + ERROR_MESSAGE()
    END CATCH
    
    GO
    /*Plan now Cached*/
    
    SELECT query_plan
    FROM   sys.dm_exec_cached_plans cp
           OUTER APPLY sys.dm_exec_sql_text(plan_handle) t
           OUTER APPLY sys.dm_exec_query_plan(plan_handle) qp
    WHERE  t.text LIKE '%IDontExist%'
    OPTION (RECOMPILE)
    
    GO
    
    BEGIN TRY
     INSERT INTO IDontExist(ProductID)
     VALUES(1)
    END TRY
    BEGIN CATCH
    SELECT 'There was an error! ' + ERROR_MESSAGE()
    END CATCH
    
    GO
    

    INSERT 语句被自动参数化。

    如果您将 Select * from IDontExist 语句更改为 Select * from IDontExist WHERE ProductID = 1,这也会自动参数化,并且它们的行为相同。

    我不确定为什么自动参数化在这里会有所不同。我认为这是由下面的extract from BOL 解释的。

    当以下类型的错误发生在与TRY…CATCH 构造相同的执行级别时,CATCH 块不会处理这些类型的错误... [那些] 在语句级重新编译期间发生...如果在 TRY 块内的较低执行级别(例如,在执行 sp_executesql 或用户定义的存储过程时)编译或语句级重新编译期间发生错误,错误 发生在比TRY…CATCH 构造更低的级别,将由关联的CATCH 块处理。

    我认为该语句的自动参数化意味着它会在较低的执行级别重新编译并且是可捕获的。

    【讨论】:

      【解决方案5】:

      现在我们对为什么会发生这种情况有了所有的解释。让我们看看问题的实际解决方案。

      首先让我们将上面@d-k-mulligan 提出的语句转化为存储过程。

      IF OBJECT_ID('dbo.prcIDontExistINSERT', 'P') IS NOT NULL DROP PROCEDURE dbo.prcIDontExistINSERT
      GO
      CREATE PROCEDURE dbo.prcIDontExistINSERT 
      AS
      BEGIN TRY
       INSERT INTO IDontExist(ProductID)
       VALUES(1)
      END TRY
      BEGIN CATCH
        SELECT 'There was an error! ' + ERROR_MESSAGE()
      END CATCH
      GO
      
      IF OBJECT_ID('dbo.prcIDontExistSELECT', 'P') IS NOT NULL DROP PROCEDURE dbo.prcIDontExistSELECT
      GO
      CREATE PROCEDURE dbo.prcIDontExistSELECT 
      AS
      BEGIN TRY
        SELECT * FROM IDontExist
      END TRY
      BEGIN CATCH
        SELECT 'There was an error! ' + ERROR_MESSAGE()
      END CATCH
      GO
      

      如果我们运行其中任何一个,我们都会看到相同的错误。

      EXEC dbo.prcIDontExistINSERT
      EXEC dbo.prcIDontExistSELECT
      
      Msg 208, Level 16, State 1, Procedure prcIDontExistSELECT, Line 4
      Invalid object name 'IDontExist'.
      

      现在的解决方案是创建错误处理包装 proc,其唯一目的是从上面的原始 proc 中捕获任何导致对象未找到错误的错误。

      IF OBJECT_ID('dbo.prcIDontExistInsert_ERROR_HANDLER', 'P') IS NOT NULL DROP PROCEDURE dbo.prcIDontExistInsert_ERROR_HANDLER
      GO
      CREATE PROCEDURE dbo.prcIDontExistInsert_ERROR_HANDLER 
      AS
      BEGIN TRY
       EXEC dbo.prcIDontExistINSERT
      END TRY
      BEGIN CATCH
        SELECT 'There was an error! ' + ERROR_MESSAGE()
      END CATCH
      GO
      
      IF OBJECT_ID('dbo.prcIDontExistSELECT_ERROR_HANDLER', 'P') IS NOT NULL DROP PROCEDURE dbo.prcIDontExistSELECT_ERROR_HANDLER
      GO
      CREATE PROCEDURE dbo.prcIDontExistSELECT_ERROR_HANDLER 
      AS
      BEGIN TRY
       EXEC dbo.prcIDontExistSELECT
      END TRY
      BEGIN CATCH
        SELECT 'There was an error! ' + ERROR_MESSAGE()
      END CATCH
      GO
      

      最后,让我们运行我们的任一错误处理过程并查看我们期望的消息。

      EXEC dbo.prcIDontExistInsert_ERROR_HANDLER
      EXEC dbo.prcIDontExistSELECT_ERROR_HANDLER
      
      There was an error! Invalid object name 'IDontExist'.
      

      注意:Kalman Toth 在这里完成了所有艰苦的研究工作: http://www.sqlusa.com/articles2008/trycatch/

      【讨论】:

        【解决方案6】:

        使用动态 sql 的解决方法。也许它会对某人有所帮助。

        begin try
            exec('
                insert into IDontExist(ProductID)
                values(1)
            ')
        end try
        begin catch
            select 'There was an error! ' + error_message()
        end catch
        

        【讨论】:

          猜你喜欢
          • 2011-08-26
          • 2011-02-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多