我的表格中有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
列更新时违反了约束。
实现我在这里尝试做的最好的方法是什么?
答案 0 :(得分:2)
现在,根据CREATE TABLE的MSDN页面:
如果表具有FOREIGN KEY或CHECK CONSTRAINTS和触发器,则在执行触发器之前评估约束条件。
这排除了使用“INSTEAD OF”触发器的可能性。
您应该删除CHECK CONSTRAINT,因为最终不需要它,因为AFTER触发器本身可以提供相同的规则强制执行:
您可以使用另一个块来检查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)
返回“true”。
您还可以使用列级DENY处理触发器之外的“规则”部分:
DENY UPDATE ON MyTable (DateTimeProcessed) TO {User and/or Role};