【问题标题】:"Instead of delete trigger" triggers two times“代替删除触发器”触发两次
【发布时间】:2021-02-11 17:01:32
【问题描述】:

我正在使用MS SQL Server 2016,在那里我实现了instead of delete 触发器。它看起来像这样:

ALTER TRIGGER MyTrigger ON MyTable INSTEAD OF DELETE AS 
BEGIN
    IF --some condition
    BEGIN
        RAISERROR ('Error msg', 16, 1)
        ROLLBACK TRAN
        RETURN
    END
    DELETE MyTable FROM MyTable JOIN deleted ON MyTable.id = deleted.id
END

如果我在表 'MyTable' 上执行 DELETE 语句并且不满足 if 中的条件,则在 if 块之后执行 DELETE 语句。这是绝对正确的。但是在 SSMS 的控制台中,DELETE 语句被执行了两次。所以在控制台中写了以下内容:

(1 rows affected)
(1 rows affected)

我不明白为什么。为什么 SSMS 两次指示一行受到影响?我使用SSMS版本15.0.18338.0

【问题讨论】:

  • 似乎是一种有趣的删除方式,为什么不使用delete from MyTable where id in (select id from Deleted)
  • @DaleK 我也以这种方式尝试过。仍然得到相同的结果。

标签: sql-server tsql triggers ssms


【解决方案1】:

这是因为有 2 组数据效果,TRIGGER 之外的集合,然后又在其中,因为初始数据集本身不执行 DML 操作。如果您不想看到后者的计数,请将NOCOUNT 转为ON。当然,这意味着如果您的 TRIGGER 中影响的行数较少,您将不会在 SSMS 的输出中知道它(但无论如何它只是提供信息)。

强烈建议您不要在TRIGGER 内部使用ROLLBACK,在TRIGGER 之外处理事务,而不是在内部。 RAISERROR 也不推荐,您应该使用 THROW 进行新的开发工作(自 2012 年以来一直推荐!)。这导致TRIGGER 如下所示:

CREATE OR ALTER TRIGGER MyTrigger ON dbo.MyTable INSTEAD OF DELETE AS 
BEGIN
    SET NOCOUNT ON;

    IF EXISTS (SELECT 1 FROM deleted WHERE SomeVal = 'Nonsense')
        THROW 95302, N'Error Msg', 16; --Use an error number appropriate for you
    ELSE
        DELETE MT FROM dbo.MyTable MT JOIN deleted ON MT.id = deleted.id;
END;
GO

【讨论】:

  • 在触发器中不使用rollback的原因是什么?
  • 因为整个语句已经与它之外的语句处于隐式事务中,@DaleK。执行ROLLBACK 之类的操作实际上会为此类场景生成一个额外的错误The transaction ended in the trigger. The batch has been aborted.,这意味着您将无法从触发器外部使用Error MsgTHROW 错误,然后您可以同时使用所述错误 ROLLBACK 事务(如果需要)(THROW 状态为 16 通常会导致外部隐式回滚交易)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-29
相关资源
最近更新 更多