以前写过一篇文章,描述了当结论不从表中真正删除,而是利用DELETE_FLAG来表示删除时,无法再利用Oracle的主键键进行约束。不过前两天newkid版主巧妙了利用了11g的虚拟列来解决了这个问题。
这里给出完整的虚拟列实现的主子表DELETE_FLAG方式的外键约束。
利用11G的虚拟列实现复杂的外键约束:http://www.itpub.net/thread-1322611-1-1.html
无法利用主外键时控制主子表的并发访问(一):http://yangtingkun.itpub.net/post/468/453295
无法利用主外键时控制主子表的并发访问(二):http://yangtingkun.itpub.net/post/468/453397
无法利用主外键时控制主子表的并发访问(三):http://yangtingkun.itpub.net/post/468/453590
看一个简单的例子:
SQL> CREATE TABLE T_FATHER
2 (ID NUMBER,
3 NAME VARCHAR2(30),
4 DELETE_FLAG VARCHAR2(1));
表已创建。
SQL> CREATE TABLE T_CHILDREN
2 (ID NUMBER,
3 FID NUMBER,
4 F_FLAG AS (CAST (DECODE(DELETE_FLAG, 'Y', NULL, 'N') AS VARCHAR2(1))),
5 DELETE_FLAG VARCHAR2(1));
表已创建。
表中包含了DELETE_FLAG,数据并不会真正的删除,当删除数据的时候,将DELETE_FLAG置为Y。显然普通的主外键无法满足这种设计的需要,因为将主表的记录删除的操作只是修改DELETE_FLAG列,而主表的记录仍然存在,因此子表新增的记录仍然可以参考主表已经产生的记录。
不过利用子表的虚拟列来建立外键越是,可以解决这个问题:
SQL> ALTER TABLE T_FATHER ADD
2 PRIMARY KEY (ID, DELETE_FLAG);
表已更改。
SQL> ALTER TABLE T_CHILDREN ADD
2 FOREIGN KEY (FID, F_FLAG)
3 REFERENCES T_FATHER;
表已更改。
下面测试一下外键是否可以正常运作:
SQL> INSERT INTO T_CHILDREN (ID, FID, DELETE_FLAG) VALUES (1, 1, 'N');
INSERT INTO T_CHILDREN (ID, FID, DELETE_FLAG) VALUES (1, 1, 'N')
*
第 1 行出现错误:
ORA-02291: 违反完整约束条件 (TEST.SYS_C0011285) - 未找到父项关键字
SQL> INSERT INTO T_FATHER VALUES (1, 'A', 'N');
已创建 1 行。
SQL> INSERT INTO T_CHILDREN (ID, FID, DELETE_FLAG) VALUES (1, 1, 'N');
已创建 1 行。
SQL> INSERT INTO T_CHILDREN (ID, FID, DELETE_FLAG) VALUES (2, 1, 'Y');
已创建 1 行。
SQL> UPDATE T_FATHER
2 SET DELETE_FLAG = 'Y'
3 WHERE ID = 1;
UPDATE T_FATHER
*
第 1 行出现错误:
ORA-02292: 违反完整约束条件 (TEST.SYS_C0011285) - 已找到子记录
SQL> UPDATE T_CHILDREN
2 SET DELETE_FLAG = 'Y'
3 WHERE ID = 1;
已更新 1 行。
SQL> UPDATE T_FATHER
2 SET DELETE_FLAG = 'Y'
3 WHERE ID = 1;
已更新 1 行。
SQL> INSERT INTO T_CHILDREN (ID, FID, DELETE_FLAG) VALUES (3, 1, 'N');
INSERT INTO T_CHILDREN (ID, FID, DELETE_FLAG) VALUES (3, 1, 'N')
*
第 1 行出现错误:
ORA-02291: 违反完整约束条件 (TEST.SYS_C0011285) - 未找到父项关键字
SQL> INSERT INTO T_CHILDREN (ID, FID, DELETE_FLAG) VALUES (3, 1, 'Y');
已创建 1 行。
SQL> UPDATE T_FATHER
2 SET DELETE_FLAG = 'N'
3 WHERE ID = 1;
已更新 1 行。
SQL> UPDATE T_CHILDREN
2 SET DELETE_FLAG = 'N'
3 WHERE ID = 2;
已更新 1 行。
SQL> COMMIT;
提交完成。
可以看到,利用虚拟列配合主表的DELETE_FLAG建立的联合主外键,完全满足这种虚拟删除的主子表关系,可惜的是,只有11g采用虚拟列的功能,11g以前,必须利用触发器来构造真实的表列。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/4227/viewspace-667760/ ,如需转载,请注明出处,否则将追究法律责任。