繁体   English   中英

提交和引发错误

[英]Commit and Raiserror in a trigger

也许有更好的方法可以做到这一点。 我想要的目的是让SQL Server向我提出两种类型的错误:更新表时,警告和来自触发器的错误。 如果SQL Server返回警告,则触发器应提交但向用户显示警告(使用.NET-优选通过SQL Exception,仅在严重性> 10时才会引发),并且如果它是ERROR,则触发器应回滚并显示ERROR给用户(通过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可以引发2种类型的错误:

 RAISERROR(@msg, 15, 120)  -- warning
 or RAISERROR(@msg, 15, 121)  -- error

我不能使用严重性<15的原因是因为我希望在.NET中显示警告(如果是警告或错误,我可以根据状态和严重性进行决定)。 .NET SqlException仅在严重性> 10时引发。

 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是消息,而不是错误号,所以您的用法还可以,所以忽略此 ]

有关此主题的详细讨论,请参见异常处理和嵌套事务 您将在此处看到解释了为什么您的实现存在缺陷的原因(它不应该检查XACT_STATE()),并且链接的文章提供了更好的实现。

现在回到您的痛点:您需要了解SQL Server错误严重性模型。 严重性高于10时引发的错误是错误,并且ADO.Net将为其引发异常。 严重性低于10引发的错误是参考消息,ADO.NEt将为其引发SqlConnection.InfoMessage事件。 另请参见不同的RAISERROR严重性级别是什么意思?

因此,问题的真正根源不是触发器,而是sp_CheckTransaction存储过程。 对于错误,它应该提高严重性16,对于警告应该提高严重性0:

RAISERROR(@msg, 0, 120)  -- warning

要么

RAISERROR(@msg, 16, 121)  -- error

并使用SqlConnection.InfoMessage拦截警告。 尝试将状态用作严重性将使您无处快速。

您的触发器可能会捕获并重新抛出该错误,具体取决于许多因素,但是您极不可能需要。 如果确实捕获到异常,则需要引发一个新的错误,并且需要适当的逻辑来处理严重性(针对错误提高16,对于警告提高0)。 请勿在T-SQL代码中使用16和0以外的其他严重性。

附带说明,从下一版本的SQL Server开始,您将能够使用简单的THROW; 重新引发原始异常,就像在.Net异常处理中一样。 更多信息SQL Server v.Next(Denali):探索THROW

我建议不要使用这种设计。 这使您难以测试代码,但是如果由于某种原因而以这种方式坚持执行此操作,请按以下步骤操作。

raiserror('foo',1,1)将引发一个错误,但不会导致触发器的执行停止。 如果严重性大于10,我相信它会将事务回滚。

http://msdn.microsoft.com/en-us/library/ms177497.aspx

然后,您必须使用SqlConnection来显示错误消息。

你有没有尝试过:

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 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM