65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Transact-SQL 中使用 TRY...CATCH

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.82/5 (16投票s)

2005 年 10 月 26 日

4分钟阅读

viewsIcon

256236

我将讨论 SQL SERVER 2005 (Yukon) 的 Try & catch 错误处理功能。

引言

在 Transact-SQL 中使用 TRY...CATCH

本文目的

我将讨论 SQL SERVER 2005 中用于错误处理的新功能 (TRY...CATCH)

Microsoft 在 SQL Server 2005 的 T-SQL 中引入了用于错误处理的 TRY & CATCH 语法 (TAC)。.NET 之前肯定也使用过此语法,因为 TAC 最初是这样引入的,现在他们将其引入 SQL。

工作原理

正如我上面提到的,TAC 块与我们在其他 .net 语言中使用的方法非常相似。思路很简单

当在 TRY 块中包含的 Transact-SQL 语句中检测到错误条件时,控制会传递到 CATCH 块,在那里可以对其进行处理。

Begin Try 
 The_Query_for_which_we_need_to_do_the_ Error_Handling 
End Try 
Begin Catch 
   
  If there is some error in the query within the Try block, this flow 
  will be passed to this Catch block. 
  
End catch 

如果在 TRY 块内没有错误,控制会传递到关联的 END CATCH 语句之后的语句。如果 END CATCH 语句是存储过程或触发器的最后一条语句,控制会传递到调用存储过程或触发器的语句。

TRY/CATCH 块是否对所有错误都有效?

不,请注意以下几点

  • TRY 块后面必须紧跟 CATCH 块。
  • TRY…CATCH 构造可以嵌套,这意味着 TRY…CATCH 构造可以放在其他 TRY 和 CATCH 块中。当嵌套的 TRY 块中发生错误时,程序控制会转移到与嵌套 TRY 块关联的 CATCH 块。
BEGIN TRY           
      Print ' I am level 1 '
          BEGIN TRY
            Print ' I am level 2 '
            SELECT 1/0;    
          END TRY
          BEGIN CATCH    
   SELECT 
   ERROR_NUMBER() AS ErrorNumber,
   ERROR_SEVERITY() AS ErrorSeverity,
   ERROR_STATE() as ErrorState,
   ERROR_PROCEDURE() as ErrorProcedure,
   ERROR_LINE() as ErrorLine,
   ERROR_MESSAGE() as ErrorMessage;   
    Print ' I am going out level 2   ' 
         END CATCH
    Print ' I am level 1 again '    
END TRY
BEGIN CATCH
    -- Execute the error retrieval routine.
        SELECT 
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_SEVERITY() AS ErrorSeverity,
        ERROR_STATE() as ErrorState,
        ERROR_PROCEDURE() as ErrorProcedure,
        ERROR_LINE() as ErrorLine,
        ERROR_MESSAGE() as ErrorMessage;    
END CATCH

输出

 I am level 1 
 I am level 2 
----------- 
(0 row(s) affected)
ErrorNumber ErrorSeverity ErrorState ErrorProcedure ErrorLine ErrorMessage
8134        16            1          NULL               5     Divide by zero 
                                                            error encountered. 
(1 row(s) affected) 
 I am going out level 2   
 I am level 1 again 

In the code above the error is thrown from the Level 2 TAC block 
  • 要处理给定 CATCH 块内发生的错误,请在指定的 CATCH 块内编写一个 TRY…...CATCH 块。
  • 严重性为 20 或更高且导致数据库引擎终止连接的错误将不会被 TRY…CATCH 块处理。但是,只要连接没有终止,TRY…CATCH 就会处理严重性为 20 或更高的错误。
  • 严重性为 10 或更低的错误被视为警告或信息性消息,不会被 TRY…CATCH 块处理。

从以上两个陈述可以清楚地看出

Try & Catch 块将适用于

严重性为 10 < TRY & Block <= 严重性为 20 的错误

错误函数

TRY…CATCH 使用错误函数来捕获错误信息。

ERROR_NUMBER() 返回错误号。

ERROR_MESSAGE() 返回错误消息的完整文本。文本包括为任何可替换参数提供的值,例如长度、对象名称或时间。

ERROR_SEVERITY() 返回错误严重性。

ERROR_STATE() 返回错误状态号。

ERROR_LINE() 返回导致错误的例程内的行号。

ERROR_PROCEDURE() 返回发生错误的存储过程或触发器的名称。

错误信息可以通过这些函数在 TRY…CATCH 构造的 CATCH 块的任何作用域内检索。如果在 CATCH 块的作用域之外调用,错误函数将返回 NULL。错误函数可以在存储过程内引用,并且可以在存储过程在 CATCH 块内执行时用于检索错误信息。这样做,就不需要在每个 CATCH 块中键入错误处理代码了。

TRY / CATCH 块是否会捕获编译错误,例如导致批处理无法执行的语法错误?

不,TAC 块不会捕获编译错误,除非它以动态查询的形式调用,或者在某个 SP 中。

在下面的代码中,表不存在,因此在编译代码时会出现错误。TAC 块在这里不起任何作用。

BEGIN TRY
    -- This PRINT statement will run since the error
    -- occurs at the SELECT statement.
    PRINT N'Starting execution'
   
   SELECT * FROM NonExistentTable
    -- This SELECT statement will generate an object name
    -- resolution error since the table does not exist. 
      
END TRY
BEGIN CATCH
    SELECT 
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
GO 
OutPut : 
Starting execution
Msg 208, Level 16, State 1, Line 6
Invalid object name 'NonExistentTable'.
我将尝试使用动态 SQL 来实现上述逻辑。这样做之后,代码将编译,但会引发一个错误,该错误将被 TAC 块捕获。

动态查询

BEGIN TRY
    -- This PRINT statement will run since the error
    -- occurs at the SELECT statement.
    PRINT N'Starting execution';
    DECLARE @SQL NVARCHAR(2000) 
    SET @SQL = 'SELECT * FROM NonExistentTable;'
    -- This SELECT statement will generate an object name
    -- resolution error since the table does not exist.
   EXEC sp_executesql  @SQL 
      
END TRY
BEGIN CATCH
    SELECT 
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
GO 

Output : 
Starting execution
ErrorNumber ErrorMessage
----------- ------
208         Invalid object name 'NonExistentTable'. 
(1 row(s) affected)

处理事务

在 TRY 语句中,可能会打开一个事务,但由于某种原因,语句转到了相应的 catch 块,那么打开的事务将如何处理。

在这种状态下,事务获得的锁将得以保留,连接也将保持打开状态。事务的工作不会被撤销,直到发出 ROLLBACK 语句。

CATCH 块中的代码应使用 XACT_STATE 函数测试事务的状态。如果会话有不可提交的事务,XACT_STATE 将返回 -1。CATCH 块不得执行任何会生成日志写入的操作(如果 XACT_STATE 返回 -1)。下面的代码示例生成了一个 DDL 语句错误,并使用 XACT_STATE 来测试事务的状态,以便采取最适当的行动。

CREATE TABLE TEMP ( A INT ) 
BEGIN TRY 
       -- BEGIN   Tran   
         ALTER TABLE TEMP
            DROP COLUMN author                 
         COMMIT TRAN 
END TRY
BEGIN CATCH
    -- Execute the error retrieval routine.
        SELECT 
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_SEVERITY() AS ErrorSeverity,
        ERROR_STATE() as ErrorState,
        ERROR_PROCEDURE() as ErrorProcedure,
        ERROR_LINE() as ErrorLine,
        ERROR_MESSAGE() as ErrorMessage;   
        PRINT ' *****Value of XACT_STATE ****'+CONVERT(VARCHAR,XACT_STATE()) 
END CATCH
GO 
Output 
ErrorNumber ErrorSeverity ErrorState  ErrorProcedure  ErrorLine   ErrorMessage
----------- ------------- ----------- --------------------------------------
3930        16            1           NULL    3       The current transaction
                                                      cannot be committed and 
                                                      cannot support
                                                      operations that write
                                                      to the log file. Roll 
                                                      back the transaction.
(1 row(s) affected) 
 *****Value of XACT_STATE ****-1
© . All rights reserved.