在 SQL 服务器中尝试捕获?

Try-Catch in SQL Server?

所以我最近开始在大学 SQL 服务器中处理事务和错误处理,我 运行 遇到了这个我的老师无法解释的问题。当我尝试执行此块时:

Begin Transaction TRA
  Begin Try
    Alter Table history
    Add Primary Key (employee_id,date_beg)
  End Try
  Begin catch
    If @@trancount>0
    Select ERROR_NUMBER() Error, ERROR_MESSAGE() Mensaje
  End Catch
Commit

我收到以下错误消息:

Msg 8111, Level 16, State 1, Line 2
Cannot define PRIMARY KEY constraint on nullable column in table 'history'.

Msg 1750, Level 16, State 0, Line 2
Could not create constraint. See previous errors.

看来代码没有像预期的那样捕获错误。

如果你能帮助我,我将不胜感激!

根据 BOL,Try Catch 在以下情况下不起作用:

an error that occurs during statement-level recompilation will not prevent the batch from compiling, but it will terminate the batch as soon as recompilation for the statement fails. For example, if a batch has two statements and the second statement references a table that does not exist, deferred name resolution causes the batch to compile successfully and start execution without binding the missing table to the query plan until that statement is recompiled. The batch stops running when it gets to the statement that references the missing table and returns an error. This type of error will not be handled by a TRY…CATCH construct at the same level of execution at which the error occurred.

Methinx 这就是问题所在。

根据 documentation on TRY...CATCH,我认为您的 CATCH 块应该已经捕获了错误。

这些是错误不会在 TRY...CATCH 块中被捕获的记录原因,其中 none 似乎适用于您的情况:

TRY…CATCH constructs do not trap the following conditions:

  • Warnings or informational messages that have a severity of 10 or lower.
  • Errors that have a severity of 20 or higher that stop the SQL Server Database Engine task processing for the session. If an error occurs that has severity of 20 or higher and the database connection is not disrupted, TRY…CATCH will handle the error.
  • Attentions, such as client-interrupt requests or broken client connections.
  • When the session is ended by a system administrator by using the KILL statement.

The following types of errors are not handled by a CATCH block when they occur at the same level of execution as the TRY…CATCH construct:

  • Compile errors, such as syntax errors, that prevent a batch from running.
  • Errors that occur during statement-level recompilation, such as object name resolution errors that occur after compilation because of deferred name resolution.

特别考虑最后两个原因,这不是编译错误,也不是延迟名称解析错误,因为您的 table 名称解析得很好。这确实是一个运行时错误,应该被捕获。

然而,尽管它在这里不适用,但您会发现以下信息为您的案例提供了可行的解决方案:

If an error occurs during compilation or statement-level recompilation at a lower execution level (for example, when executing sp_executesql or a user-defined stored procedure) inside the TRY block, the error occurs at a lower level than the TRY…CATCH construct and will be handled by the associated CATCH block.

应用以上信息,您会发现可以通过将脚本更改为以下两个选项之一来捕获错误:

选项 1:sp_executesql

Begin Transaction TRA
  Begin Try
    exec sp_executesql 'Alter Table history Add Primary Key (employee_id,date_beg)';
  End Try
  Begin catch
    If @@trancount>0
    Select ERROR_NUMBER() Error, ERROR_MESSAGE() Mensaje
  End Catch
Commit

选项 2:将 alter 语句包装在单独的存储过程中

create procedure WrapAlterStatementInSP as
Alter Table history Add Primary Key (employee_id,date_beg);
go

Begin Transaction TRA
  Begin Try
    exec WrapAlterStatementInSP;
  End Try
  Begin catch
    If @@trancount>0
    Select ERROR_NUMBER() Error, ERROR_MESSAGE() Mensaje
  End Catch
Commit

希望这对您有所帮助。但我确实觉得当前行为中存在错误,或者文档中缺少信息。