此类场景的常见建议是使用可延迟约束。但是,我认为这些情况几乎总是应用程序逻辑或数据模型的失败。例如,如果我们将其作为两个语句执行,则在同一事务中插入子记录和父记录可能会出现问题:
我的测试数据:
SQL> select * from t23 order by id, parent_id
2 /
ID PARENT_ID NAME
---------- ---------- ------------------------------
110 parent 1
111 parent 2
210 110 child 0
220 111 child 1
221 111 child 2
222 111 child 3
6 rows selected.
SQL>
错误的做事方式:
SQL> insert into t23 (id, parent_id, name) values (444, 333, 'new child')
2 /
insert into t23 (id, parent_id, name) values (444, 333, 'new child')
*
ERROR at line 1:
ORA-02291: integrity constraint (APC.T23_T23_FK) violated - parent key not
found
SQL> insert into t23 (id, parent_id, name) values (333, null, 'new parent')
2 /
1 row created.
SQL>
但是,Oracle 支持多表 INSERT 语法,它允许我们在同一语句中插入父记录和子记录,从而消除了对可延迟约束的需要:
SQL> rollback
2 /
Rollback complete.
SQL> insert all
2 into t23 (id, parent_id, name)
3 values (child_id, parent_id, child_name)
4 into t23 (id, name)
5 values (parent_id, parent_name)
6 select 333 as parent_id
7 , 'new parent' as parent_name
8 , 444 as child_id
9 , 'new child' as child_name
10 from dual
11 /
2 rows created.
SQL>
你的情况是类似的:你想更新父记录的主键,但是因为子记录的存在而不能更新:而你不能更新子记录,因为没有父键。第 22 条军规:
SQL> update t23
2 set id = 555
3 where id = 111
4 /
update t23
*
ERROR at line 1:
ORA-02292: integrity constraint (APC.T23_T23_FK) violated - child record found
SQL> update t23
2 set parent_id = 555
3 where parent_id = 111
4 /
update t23
*
ERROR at line 1:
ORA-02291: integrity constraint (APC.T23_T23_FK) violated - parent key not
found
SQL>
再一次,解决方案是在单个语句中执行此操作:
SQL> update t23
2 set id = decode(id, 111, 555, id)
3 , parent_id = decode(parent_id, 111, 555, parent_id)
4 where id = 111
5 or parent_id = 111
6 /
4 rows updated.
SQL> select * from t23 order by id, parent_id
2 /
ID PARENT_ID NAME
---------- ---------- ------------------------------
110 parent 1
210 110 child 0
220 555 child 1
221 555 child 2
222 555 child 3
333 new parent
444 333 new child
555 parent 2
8 rows selected.
SQL>
UPDATE 语句中的语法有点笨拙,但通常是杂乱无章的。关键是我们不应该经常更新主键列。事实上,由于不变性是“主键性”的特征之一,我们根本不需要更新它们。需要这样做是数据模型的失败。避免此类故障的一种方法是使用合成(代理)主键,并通过唯一约束简单地强制自然(也称为业务)键的唯一性。
那么为什么 Oracle 提供可延迟的约束呢?当我们进行数据迁移或批量数据上传时,它们很有用。它们允许我们在没有临时表的情况下清理数据库中的数据。我们真的不应该将它们用于常规应用程序任务。