T-SQL中的TRY CATCH块

7

我遇到了一个存储过程,在更新尝试之后紧接着有以下错误处理块。以下是存储过程的最后几行。

这样做是否有任何好处?在我看来,这段代码只是重新抛出了捕获的同一错误,没有添加任何价值,如果省略Try块,代码很可能会完全相同。

如果省略TRY块,结果SP的行为是否有任何区别?

BEGIN CATCH

SELECT @ErrMsg = ERROR_MESSAGE(), @ErrSev = ERROR_SEVERITY(), @ErrState = ERROR_STATE()
        RAISERROR (@ErrMsg, @ErrSev, @ErrState)

END CATCH

我能想到的唯一区别是,如果没有CATCH抛出新异常,行号信息和错误号信息会更准确,因此在我看来实际上似乎会减少价值... - Martin Smith
我总是更喜欢在前端使用自定义错误消息,而不是SQL生成的错误消息。这种方式将为您提供逻辑和技术错误,您已经理解并提到了它们。 - Chris
4个回答

2
通常我们在存储过程中的做法是像这样编写catch块。
BEGIN CATCH
  DECLARE @i_intErrorNo int          
  DECLARE @i_strErrorMsg nvarchar(1000)          
  DECLARE @i_strErrorProc nvarchar(1000)          
  DECLARE @i_intErrorLine int          

  SELECT @i_intErrorNo=Error_Number()          
  SELECT @i_strErrorMsg=Error_Message()          
  SELECT @i_strErrorProc=Error_Procedure()          
  SELECT @i_intErrorLine=Error_Line()   

  INSERT INTO error table ////// Insert statement. 

END CATCH 

这是我们用来存储错误的方法。为了向用户显示适当的信息,我总是使用存储过程的输出参数来展示错误的详细/必要原因。


2

除了任何返回信息中“行错误发生在”部分将引用RAISERROR行而不是实际出错的行之外,没有任何区别。这样做的主要原因正如@Chris所说,是为了允许您以编程方式使用/操作错误数据。


当然,你是正确的。我没有仔细阅读它,并且假设任何这样做的人都会捕获和追踪实际的错误编号。 - Philip Kelley

1

如果您查看RAISERROR的MSDN页面,则会看到以下一般描述:

生成错误消息并启动会话的错误处理。 RAISERROR可以引用存储在sys.messages目录视图中的用户定义消息或动态构建消息。该消息作为服务器错误消息返回给调用应用程序或TRY…CATCH结构的关联CATCH块。

似乎“调用应用程序”将获得错误消息。可能是由于安全问题或仅因为调用应用程序不需要知道其他可以添加的选项(这可能过于冗长或过于详细),存储过程的创建者希望仅报告错误消息、严重性和状态。


所有的“额外信息”都包含在ERROR_MESSAGE()返回的字符串中。要混淆它,您必须用自己不太详细的错误消息替换它,而这个例程并没有这样做。 - Philip Kelley

0

有微妙的差别,如下所示。

首先设置以下内容:

CREATE TABLE TMP
( ROW_ID int NOT NULL,
  ALTER TABLE TMP ADD CONSTRAINT PK_TMP PRIMARY KEY CLUSTERED (ROW_ID)
)
GO
CREATE PROC pTMP1
AS
BEGIN TRY
  INSERT INTO TMP VALUES(1)
  INSERT INTO TMP VALUES(1)
  INSERT INTO TMP VALUES(2)
END TRY
BEGIN CATCH
  DECLARE @ErrMsg varchar(max)= ERROR_MESSAGE(),
          @ErrSev int = ERROR_SEVERITY(),
          @ErrState int = ERROR_STATE()
        RAISERROR (@ErrMsg, @ErrSev, @ErrState)
END CATCH
GO
CREATE PROC pTMP2
AS
  INSERT INTO TMP VALUES(1)
  INSERT INTO TMP VALUES(1)
  INSERT INTO TMP VALUES(2)
GO

现在运行以下命令:

SET NOCOUNT ON
DELETE TMP
exec pTMP1
SELECT * FROM TMP
DELETE TMP
exec pTMP2
SELECT * FROM TMP
SET NOCOUNT OFF
--Cleanup
DROP PROCEDURE pTMP1
DROP PROCEDURE pTMP2
DROP TABLE TMP

你应该得到以下结果:
Msg 50000, Level 14, State 1, Procedure pTMP1, Line 12
Violation of PRIMARY KEY constraint 'PK_TMP'. Cannot insert duplicate key in object 'dbo.TMP'. The duplicate key value is (1).
ROW_ID
-----------
1

Msg 2627, Level 14, State 1, Procedure pTMP2, Line 4
Violation of PRIMARY KEY constraint 'PK_TMP'. Cannot insert duplicate key in object 'dbo.TMP'. The duplicate key value is (1).
The statement has been terminated.
ROW_ID
-----------
1
2

请注意,使用TRY..CATCH版本时,第三个INSERT语句没有执行,而pTMP2过程执行了。这是因为一旦出现错误,控制就会跳转到CATCH
注意:pTMP2的行为受XACT_ABORT设置的影响。
结论
使用TRY..CATCH的好处取决于您如何管理事务边界。
- 如果在任何错误上回滚,则更改将被撤消。但这并不能消除附加处理等副作用。注意:如果不同的会话同时使用WITH(NOLOCK)查询TMP,它甚至可能能够观察到临时更改。 - 但是,如果您不打算回滚事务,则可能会发现该技术非常重要,可以防止早期错误导致应用某些数据更改。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接