我知道 SQL 约束可以强制数据满足有效性标准。但是,诸如“学生的成绩只能在'finalized'标志为假时更新”这样的标准呢?此类更新标准是否必须由应用程序处理?
3 回答
简短回答:不,当 Finalized 为“真”时,SQL 约束本身不能阻止对列 Grade 的更改(但允许更改)。
有几种 SQL 约束:CHECK、DEFAULT、NOT NULL、UNIQUE、主键和外键。
这些中的每一个都可以单独或组合地限制或影响列的值,但不能阻止对允许的值进行更新。特别是,这些约束都不能阻止基于先前的 Grade 和 Finalized 值对 Grade 和/或 Finalized 进行更新。
UPDATE 触发器可以执行此操作:比较 Grade 的新值和旧值,如果这些值不同并且 Finalized = 'true',则使用说明性错误消息回滚 UPDATE。
然而,应用程序可以而且应该更优雅地执行这样的“业务规则”。规则本身可以使用一些关于何时可以更改 Finalized 值的说明。例如,是否允许同时更改 Grade 和设置 Finalized = 'false'?触发逻辑可以处理这些细节,并且将其安装为故障保护是合理的,同时在应用程序的某处(前端/中间件/后端)中明确显示规则。
触发器、约束和附加列。
从头开始:
附加列存储要“固定”的值:
ALTER TABLE ADD SavedGrade int
约束限制了
Grade
列的变化:ALTER TABLE Students ADD CONSTRAINT CK_Grade CHECK (Finalised = 'false' OR Grade = SavedGrade)
当
Grade
列更新时,触发器会更新附加列(以下适用于 SQL Server):CREATE TRIGGER StudentsFinaliseGrade ON Students AFTER INSERT, UPDATE AS IF UPDATE(Grade) UPDATE Students SET SavedGrade = i.Grade FROM inserted i WHERE i.ID = Students.ID AND i.Grade <> i.SavedGrade
所以,只要Finalised = 'false'
,Grade
列就可以改变。更改时,该值立即存储到SavedGrade
列中。(我们SavedGrade
直接更新,否则约束不允许我们设置Finalised
为'true'
。)一旦Finalised
设置,由于约束,您不能再更改Grade
列。
IMO,我想说它应该在应用程序或存储过程(可能两者)中完成,而不是作为实际约束(除其他外,在您的具体示例中,“最终确定”的成绩并不总是意味着它实际上是最终的)。
但是,如果我将其作为约束来实现,我会使用 CHECK 约束(再次使用您的示例)
CONSTRAINT chk_grade CHECK(grade between 0 AND 100 and finalized = 0)
检查具体的语法,但这是我要开始的地方。