【问题标题】:Check constraint violation before After Update trigger fires在 After Update 触发器触发之前检查约束冲突
【发布时间】:2015-01-02 00:52:11
【问题描述】:

我有一个表,其中有一个 bit 列和一个对应的 datetime2 列,用于跟踪该标志的设置时间:

CREATE TABLE MyTable
(
    Id int primary key identity,
    Processed bit not null,
    DateTimeProcessed datetime2
)

我添加了一个检查约束如下:

ALTER TABLE MyTable 
  ADD CHECK ((Processed = 0 AND DateTimeProcessed IS NULL) 
             OR (Processed = 1 AND DateTimeProcessed IS NOT NULL))

我尝试使用AFTER UPDATE 触发器来控制DateTimeProcessed 列的设置:

CREATE TRIGGER tr_MyTable_AfterUpdate ON MyTable
AFTER UPDATE
AS
BEGIN
    IF(UPDATE(Processed))
    BEGIN
        UPDATE MyTable
        SET DateTimeProcessed = CASE
            WHEN tab.Processed = 1 THEN GETDATE()
            ELSE NULL
            END
        FROM MyTable tab
        JOIN INSERTED ins
        ON ins.Id = tab.Id
    END
END

这样做的问题是检查约束是在AFTER UPDATE 触发器运行之前强制执行的,因此在更新Processed 列时违反了约束。

实现我在这里尝试做的最好方法是什么?

【问题讨论】:

    标签: sql-server sql-server-2008 tsql check-constraints database-trigger


    【解决方案1】:

    现在,根据 CREATE TABLE 的 MSDN 页面:

    如果表有 FOREIGN KEY 或 CHECK CONSTRAINTS 和触发器,则在执行触发器之前评估约束条件。

    这也排除了使用“INSTEAD OF”触发器的可能性。

    您应该删除 CHECK CONSTRAINT,因为最终不需要它,因为 AFTER 触发器本身可以提供相同的规则执行:

    • 您已经确保在将 BIT 字段设置为 1 时设置日期字段。
    • 您的 CASE 语句已经通过清空日期字段来处理设置为 0 的 BIT 字段。
    • 您可以在IF UPDATE(DateTimeProcessed) 上检查另一个块,然后将其放回DELETED 表中的状态或抛出错误。

      • 如果将其更新回原始值,则可能需要测试递归触发器调用,如果是递归调用则退出。
      • 如果你想抛出错误,只需使用类似以下内容的内容:

        IF(UPDATE(DateTimeProcessed))
        BEGIN
           RAISERROR('Update of [DateTimeProcessed] field is not allowed.', 16, 1);
           ROLLBACK; -- cancel the UPDATE statement
           RETURN;
        END;
        

        请记住,UPDATE() 函数仅表示该字段在 UPDATE 语句中; 不是值变化的指示。因此,在 SET DateTimeProcessed = DateTimeProcessed 中执行 UPDATE 显然不会更改值,但会导致 UPDATE(DateTimeProcessed) 返回“true”。

      • 您还可以使用列级DENY 在触发器之外处理这部分“规则”:

        DENY UPDATE ON MyTable (DateTimeProcessed) TO {User and/or Role};

    【讨论】:

    • “建议“BEFORE”触发器的注释可能由于 SQL Server 没有“BEFORE”触发器而被删除”...是的,这可能是原因,不是吗: )
    • 感谢您的回答。如果DateTimeProcessed 直接更新,则抛出错误似乎是要在此处添加的最后一部分。
    • @grin0048 :是的,我曾建议在那种情况下抛出一个错误,这是你指的还是你没有看到我的更新?无论如何,我再次更新以重新排列项目符号的顺序并添加了一些细节,例如处理错误和取消的代码以及不涉及代码的新(和鲜为人知的)选项。
    • 是的,我指的是您的建议——只是指出我认为最有帮助的建议。再次感谢!
    猜你喜欢
    • 1970-01-01
    • 2013-08-26
    • 1970-01-01
    • 2020-04-17
    • 2011-03-05
    • 1970-01-01
    • 1970-01-01
    • 2014-06-28
    • 1970-01-01
    相关资源
    最近更新 更多