此约束比 CHECK 约束可以处理的更复杂。有一天,我们希望 Oracle 能够支持SQL ASSERTIONS,这是任意复杂性的约束。
同时,这可以使用物化视图 (MV) 和约束来完成(注意性能)。我blogged about this 可能多年前:您的要求与我的示例 3 非常相似。应用到你的情况下,它会是这样的:
create materialized view table_2_mv
build immediate
refresh complete on commit as
select t2.cd_customer, t2.cd_object, t1.max_number, count(*) cnt
from table_2 t2
join table_1 t1 on t1.cd_object = t2.cd_object
group by t2.cd_customer, t2.cd_object, t1.max_number;
alter table table_2_mv
add constraint table_2_mv_chk
check (cnt <= max_number)
deferrable;
纯基于触发器的解决方案在现实世界中往往会失败,因为当 2 个用户同时添加一条仅将计数达到最大值的记录时,无论成功还是提交时,都会使表中的行数超过最大值!
但是,考虑到您在 table_2 中有 2M 行的评论,这可能使上述 MV 方法无法使用,可能还有另一种涉及触发器的方法:
- 创建一个表,对来自 table_1 和 table_2 的信息进行非规范化处理,如下所示:
create table denorm as
select t2.cd_customer, t2.cd_object, t1.max_number, count(*) cnt
from table_2 t2
join table_1 t1 on t1.cd_object = t2.cd_object
group by t2.cd_customer, t2.cd_object, t1.max_number;
在 table_1 上使用一个或多个触发器以确保 denorm.max_number 始终正确 - 例如,当 table_1.max_number 更新为新值时,更新相应的 denorm 表行。
在 table_2 上使用一个或多个触发器来更新 denorm.cnt 值 - 例如添加一行时,增加denorm.cnt,删除一行时,减少它。
为 denorm 添加检查约束
alter table denorm
add constraint denorm_chk
check (cnt <= max_number);
这本质上与 MV 解决方案相同,但通过使用触发器在您进行时维护 denorm 表来避免完全刷新。它适用于多用户系统,因为对 denorm 表的更新会序列化对 table_2 的更改,因此 2 个用户无法同时修改它并破坏规则。