DEFERRABLE 约束和级联更新
从Oracle8.0 开始,我们还能够延迟约束检查,对于许多操作来说,这很有好处。首先能想到的是,
可能需要将一个主键的UPDATE 级联到子键。也许很多人会说:这没有必要,因为主键是不可变的(我就是
这些人之一),但是还有人坚持要有级联UPDATE。有了可延迟的约束,就使得级联更新成为可能。
注意一般认为,完成更新级联来修改主键是很不好的做法。这会破坏主键的意图。如果你必须做一
次级联更新来修正不对的信息,这倒是可以的;但是如果你发现自己在不停地完成级联更新,并
把这当做应用的一部分,那就是另一码事了,你应该退一步,重新考虑一下这个过程。倘若真是
这样,能你就是错把鸡毛当令箭了!
在以前的版本中,确实也可以完成CASCADE UPDATE,但是为此需要做大量的工作,而且存在某些限
制。有了可延迟的约束后,这就变得易如反掌了。代码如下:
ops$tkyte@ORA10G> insert into t values ( 2 );
1 row created.
ops$tkyte@ORA10G> update t set x = x+1;
2 rows updated.
ops$tkyte@ORA10G> create table p
2 ( pk int primary key )
3 /
Table created.
ops$tkyte@ORA10G> create table c
2 ( fk constraint c_fk
3 references p(pk)
4 deferrable
5 initially immediate
319
/ 849
我们有一个父表P,还有一个子表C。表C 引用了表P,保证这个规则的约束是C_FK(子外键)。这个
约束创建为一个DEFERRABLE 约束,但是设置为INITIALLY IMMEDIATE。这说明,可以把这个约束延迟到
COMMIT 或另外某个时间才检查。不过,默认情况下,这个约束在语句级验证。这是可延迟约束最常见的用
法。大多数现有的应用不会在COMMIT 语句上检查约束冲突,你最好也不要这么做。根据定义,表C 与一般
的表一样有正常的表现,不过我们可以显式地改变它的行为。下面,在这些表上尝试一些DML,看看会发
生什么:
由于约束是IMMEDIATE 模式,这个UPDATE 会失败。下面换个模式再试一次:
现在更新成功了。为了便于说明,下面将显示如何在提交前显式地检查了一个延迟约束,才中了解
我们所做的修改与业务规则是否一致(换句话说,检查目前确实没有违反约束)。应该在提交之前或者在
6 )
7 /
Table created.
ops$tkyte@ORA10G> insert into p values ( 1 );
1 row created.
ops$tkyte@ORA10G> insert into c values ( 1 );
1 row created.
ops$tkyte@ORA10G> update p set pk = 2;
update p set pk = 2
*
ERROR at line 1:
ORA-02292: integrity constraint (OPS$TKYTE.C_FK) violated - child record found
ops$tkyte@ORA10G> set constraint c_fk deferred;
Constraint set.
ops$tkyte@ORA10G> update p set pk = 2;
1 row updated.
320
/ 849
把控制交给程序的另外某个部分(这一部分可能不希望有延迟约束)之前做这个工作,这是一个很好的主
意:
不出所料,它会失败,并立即返回一个错误,因为我们知道以上更新会违反约束。对P 的UPDATE 没
有回滚(否则会违反语句级原子性);它仍在进行。还要注意,我们的事务仍把C_FK 当作延迟约束,因为
SET CONSTRAINT 命令失败了。下面继续将UPDATE 级联到C:
这就是级联更新的做法。注意,要延迟一个约束,必须这样来创建它们:先将其删除,再重新创建约
束,这样才能把不可延迟的约束改变为可延迟约束。
---------------------------------------Example----------------------------------------------------
create table p
( pk int primary key )
--Normally
create table c
( fk constraint c_fk
references p(pk))
--Deferrable
create table c
( fk constraint c_fk
references p(pk)
deferrable
initially immediate)
set constraint c_fk deferred
insert into p values ( 1 );
insert into c values ( 1 );
update c set fk=2;
update p set pk=2;