也许有更好的方法来做到这一点。我想要的是让SQL Server向我提出两种类型的错误:更新表时来自触发器的WARNING和ERROR。如果SQL Server返回WARNING,则触发器应该COMMIT但向用户显示警告(使用.NET - 最好通过SQL异常,只有在严重性> 10时才会引发),如果是ERROR,则触发器应该ROLLBACK并显示对用户的错误(通过SQL异常)。我的尝试(不用说这不起作用)就是有这样一个触发器:
ALTER TRIGGER [dbo].[TR_TRANSACTION_UPDATE]
ON [dbo].[tTRANSACTION]
FOR UPDATE
AS
BEGIN
...
BEGIN TRY
DECLARE @id INT ,@maxid INT
SELECT @id = 0 ,@maxid = MAX(transID) FROM INSERTED
WHILE @id < @maxid
BEGIN
SELECT @id = MIN([TransID]) FROM INSERTED WHERE [TransID] > @id
EXEC dbo.sp_CheckTransaction @TransID = @id
END
COMMIT TRANSACTION
END TRY
BEGIN CATCH
DECLARE @err_msg VARCHAR(MAX), @err_sev AS INT, @err_state AS INT
SELECT @err_msg = ERROR_MESSAGE(), @err_sev = ERROR_SEVERITY(), @err_state = ERROR_STATE()
IF @err_state <> 120 -- '120 is not a fatal error from STORED_PROC
ROLLBACK TRANSACTION
ELSE
COMMIT TRANSACTION
RAISERROR(@err_msg, @err_sev, @err_state)
END CATCH
END
此触发器不起作用,因为它认为交易无法使用。
此外,sp_CheckTransaction可以引发两种类型的错误:
RAISERROR(@msg, 15, 120) -- warning
or RAISERROR(@msg, 15, 121) -- error
我无法使用严重性的原因&lt; 15是因为我想在.NET中显示警告(如果是警告或错误,我可以根据状态和严重性来决定)。仅针对严重性&gt;引发.NET SqlException 10.
答案 0 :(得分:3)
RAISERROR(@err_msg, @err_sev, @err_state)
你不能这样做。不允许您引发系统消息,仅允许用户消息(错误号大于50000)。你必须这样提高:
RAISERROR(N'An error occured in trigger: %s %d %d',
<severity>, <state>, @err_msg, @err_sev, @err_state);
[更新:实际上,因为@err_msg
是消息,而不是错误编号,您的使用是正常的,所以请忽略此]
有关此主题的更长时间的讨论,请参阅Exception handling and nested transactions。你会看到那里解释了为什么你的实现存在缺陷(它不会检查XACT_STATE())并且链接的文章提供了更好的实现。
现在回到您的痛点:您需要了解SQL Server错误严重性模型。严重性超过10的错误是错误,ADO.Net将为它们引发异常。严重性低于10的错误是信息性消息和ADO.NEt将为他们举起SqlConnection.InfoMessage
事件。另请参阅What do the different RAISERROR severity levels mean?
因此问题的真正根源不是您的触发器,而是sp_CheckTransaction
存储过程。它应该针对错误提高严重性16,针对警告提高严重性0:
RAISERROR(@msg, 0, 120) -- warning
或
RAISERROR(@msg, 16, 121) -- error
使用SqlConnection.InfoMessage
拦截警告。尝试将状态用作严重性将使您无法快速完成任务。
您的触发器可能会捕获并重新抛出错误,具体取决于多种因素,但您不太可能需要这样做。如果你确实捕获了异常,那么你需要引发一个 new 错误,并且需要适当的逻辑来处理严重性(错误增加16,警告增加0)。切勿在T-SQL代码中使用16和0以外的任何其他严重性。
作为旁注,从下一版本的SQL Server开始,您将能够使用简单的THROW;
重新抛出原始异常,就像.Net异常处理一样。更多信息SQL Server v.Next (Denali) : Exploring THROW。
答案 1 :(得分:1)
我会建议不要这样的设计。它会让你更难以测试你的代码,但是如果你出于某种原因而被这样做了,这就是如何做到的。
raiserror('foo',1,1)会引发错误,但不会导致触发器的执行停止。如果严重程度大于10,我相信它会使事务回滚。
http://msdn.microsoft.com/en-us/library/ms177497.aspx
然后您必须使用SqlConnection来显示错误消息。
答案 2 :(得分:0)
你试过了吗?
IF @err_state <> 120 -- '120 is not a fatal error from STORED_PROC'
而不是
IF @err_state <> 120 -- '120 is not a fatal error from STORED_PROC