Sql server 在更新触发器触发之前和之后检查约束冲突

Sql server 在更新触发器触发之前和之后检查约束冲突,sql-server,sql-server-2008,tsql,check-constraints,database-trigger,Sql Server,Sql Server 2008,Tsql,Check Constraints,Database Trigger,我有一个表,它有一个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

我有一个表,它有一个
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
列的设置:

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
问题在于,check约束是在
AFTER UPDATE
触发器运行之前强制执行的,因此更新
Processed
列时违反了该约束


实现我在这里尝试的目标的最佳方式是什么?

现在,根据MSDN页面:

如果表具有外键或CHECK约束和触发器,则在执行触发器之前会计算约束条件

这也排除了使用“代替”触发器的可能性

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

  • 您已经在确保位字段设置为1时设置了日期字段
  • 您的CASE语句已在通过清空日期字段来处理设置为0的位字段
  • 如果更新(DateTimeProcessed)
,您可以使用另一个块来检查
,然后将其恢复到
已删除的表中的状态,或者抛出一个错误

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

    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)
    返回“true”

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

    拒绝向{User和/或Role}更新MyTable(DateTimeProcessed)


“建议使用“BEFORE”触发器的评论可能已被删除,因为SQL Server没有“BEFORE”触发器……是的,这可能就是原因,不是吗:)谢谢您的回答。如果直接更新了
DateTimeProcessed
,则抛出一个错误似乎是此处要添加的最后一项。@grin0048:是的,我曾建议在该场景中抛出一个错误,这是您所指的还是您没有看到我的更新?不管怎样,我再次更新以重新排列项目符号的顺序,并添加了一些细节,例如处理错误和取消的代码,以及一个新的(鲜为人知的)不涉及代码的选项。是的,我是指你的建议——只是记下我觉得哪一个最有用。再次感谢!