【问题标题】:ROLLBACK in TRIGGER causes Msg 3609TRIGGER 中的 ROLLBACK 导致 Msg 3609
【发布时间】:2016-09-28 19:55:11
【问题描述】:

我有一个 Items 表和一个 Items_Log 表来记录对 Item 表的所有更改。

CREATE TABLE [dbo].[Items] (
  [item_id] [int] IDENTITY(1,1) NOT NULL,
  [item_name] [varchar](50) NOT NULL,
  [item_desc] [varchar](250) NULL,
  [modified_by_id] [int] NOT NULL,
  [modified_date] [datetime] NOT NULL
)

CREATE TABLE [dbo].[Items_Log] (
  [item_log_id] [int] IDENTITY(1,1) NOT NULL,
  [item_id] [int] NOT NULL,
  [item_name] [varchar](50) NOT NULL,
  [item_desc] [varchar](250) NULL,
  [modified_by_id] [int] NOT NULL,
  [modified_date] [datetime] NOT NULL
)

对 Items 表的更新在 SPROC 中执行。 ([modified_by_id] 故意注释掉,如下所述)

CREATE PROCEDURE [dbo].[pUpdateItem]
  @ItemID INT,
  @Name VARCHAR(100),
  @ByID INT
AS
  UPDATE [Items] SET
    item_name = @Name,
    --modified_by_id = @ByID,
    modified_date = GETDATE()
  WHERE item_id = @ItemID;
GO

Items 表上有一个触发器,它会在旧数据以任何方式更新时记录它。

ALTER TRIGGER [dbo].[tItemsUpdate] 
   ON [dbo].[Items] 
   AFTER UPDATE
AS
BEGIN
  SET NOCOUNT ON;

  IF (UPDATE(modified_by_id) AND UPDATE(modified_date))
    BEGIN
      INSERT INTO [Items_Log]
        SELECT * FROM Deleted
    END
  ELSE
    BEGIN
      RAISERROR ('[modified_by_id] and [modified_date] must be updated.', 16, 1)
      ROLLBACK TRANSACTION
    END
END

为了测试触发器,注释掉 [modified_by_id] 以便调用 RAISERROR。我收到 2 个错误:

Msg 50000, Level 18, State 1, Procedure tItemsUpdate, Line 15
[modified_by_id] and [modified_date] must be updated.
Msg 3609, Level 16, State 1, Procedure pUpdateItem, Line 5
The transaction ended in the trigger. The batch has been aborted.

第一个错误显然是我想看到的错误,并且事务正确回滚。但我真的希望它退出而不抛出第二个错误,因为它会像这样显示给用户。

因此,根据我在其他地方看到的建议,我尝试在 SPROC 中执行 Try...Catch,并在那里正式声明事务和回滚(并将回滚从触发器中取出)。在 SPROC 中看起来像这样:

BEGIN TRY
  BEGIN TRANSACTION
    UPDATE [Items] SET
      item_name = @Name,
      --modified_by_id = @ByID,
      modified_date = GETDATE()
    WHERE item_id = @ItemID;
  COMMIT TRANSACTION
END TRY
BEGIN CATCH
  ROLLBACK TRANSACTION
END CATCH

现在更新不会发生,但我根本没有收到任何错误消息。甚至不是来自 RAISERROR 的那个。

我希望我可以简单地取消“Msg 3609”。这将使一切都按我想要的方式工作。但在这一点上,我会采用几乎所有可行的解决方案。

谁能帮我解决这个问题?

【问题讨论】:

    标签: sql sql-server triggers transactions rollback


    【解决方案1】:
    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; 
        ROLLBACK TRANSACTION;
    END CATCH;  
    

    https://msdn.microsoft.com/en-us/library/ms190358.aspx

    BEGIN CATCH  
        DECLARE @ErrorSeverity INT, @ErrorState INT, @ErrorMessage VARCHAR(100); 
        SELECT @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE(), @ErrorMessage = ERROR_MESSAGE(); 
        ROLLBACK TRANSACTION; 
        RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
    END CATCH
    

    【讨论】:

    • 好的,我以这种方式获取错误信息,但现在它不会将错误返回给我的班级。我想我可以用捕获的值再次执行 RAISERROR 吗?
    • 是的,你可能会提出新的错误,可能会添加一些信息。
    • 是的,如果我必须在几乎每个 SPROC 中执行此操作,它会有点笨重,但这基本上是我现在所拥有的:BEGIN CATCH DECLARE @ErrorSeverity INT` ` DECLARE @ErrorState INT ` ` DECLARE @ErrorMessage VARCHAR(100)` SET @ErrorSeverity = ERROR_SEVERITY() SET @ErrorState = ERROR_STATE() SET @ErrorMessage = ERROR_MESSAGE() ROLLBACK TRANSACTION; RAISERROR(@ErrorMessage,@ErrorSeverity,@ErrorState);结束捕捉'
    • 是的,我正在玩它,看看我是否可以在不声明变量的情况下做到这一点,但它看起来不像。这很混乱,但至少它有效。谢谢!
    猜你喜欢
    • 2023-03-24
    • 2015-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多