2

我有具有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列时违反了约束条件。

什么会达到什么样的,我想在这里做的最好方法是什么?现在

回答

2

,根据MSDN页CREATE TABLE

如果表的外键或检查约束和触发器,所述约束条件是在执行触发前评估。

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

您应该删除作为最终它不需要检查约束,因为触发后本身所能提供的规则相同执法:

  • 您已经确保日期场被于集BIT字段设置为1。
  • 你的CASE语句已经归零了日期字段处理位字段被设置为0。
  • 您可以使用另一个块检查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(DateTimeProcessed)返回“真”。

    • 您也可以通过使用列级DENY处理触发的“规则”之外的这部分:

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

+1

“的评论说,建议” BEFORE “触发器可能由于SQL Server没有”BEFORE“触发器而被删除”......是的,这可能是原因,不是吗:) – grin0048 2014-11-05 20:34:08

+0

感谢您的答案。如果直接更新'DateTimeProcessed'则抛出一个错误,就像在这里添加的最后一部分。 – grin0048 2014-11-05 20:44:47

+0

@ grin0048:是的,我曾建议在这种情况下抛出一个错误,那是你指的还是你没有看到我的更新?无论如何,我再次更新以重新排列项目符号的顺序并添加了一些细节,例如处理错误和取消的代码以及不涉及代码的新(不太知名)选项。 – 2014-11-05 22:02:00