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))

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

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

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

4

1 回答 1

2

现在,根据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 语句中;这并不表示价值发生了变化。因此,在执行 UPDATE 时,您SET DateTimeProcessed = DateTimeProcessed显然不会更改该值,但会导致UPDATE(DateTimeProcessed)返回“true”。

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

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

于 2014-11-05T20:29:15.063 回答