oracle 主外键管理

时间:2023-01-04 11:31:43

使用主外键约束使得数据具有完整性。

1、查询表上所有的约束

select * from user_constraints t
where t.table_name='FATHER';

2、查询具有主外键关系的表

select c.owner,c.constraint_name,c.constraint_type,c.table_name,f.owner,f.constraint_name,f.constraint_type,f.table_name
from dba_constraints c, dba_constraints f
where c.r_owner=f.owner
and c.r_constraint_name=f.constraint_name
and c.table_name='CHILD';  --查询子表CHILD对应的所有父表

3、子表中插入的记录必须在父表中存在,否则会报parent key not found

SQL> insert into child values ('datong',1);
insert into child values ('datong',1)
*
ERROR at line 1:
ORA-02291: integrity constraint (SCOTT.FK_ADDR) violated - parent key not found

4、父表的记录只有在子表中找不到才可以删除,否则会报child record found,除非子表的外键约束on delete=cascade/set null
SQL> delete from father where id=1;
delete from father where id=1
*
ERROR at line 1:
ORA-02292: integrity constraint (SCOTT.FK_ID) violated - child record found

SQL> delete from father where id=2;

1 row deleted.

SQL> commit;

Commit complete.

5、如何完全删除父表数据,如truncate、drop

SQL> truncate table father;
truncate table father
               *
ERROR at line 1:
ORA-02266: unique/primary keys in table referenced by enabled foreign keys

 

针对上面情况,可以先将father表的所有子表的引用约束disable,使用下面的sql得到禁用子表约束语句:

select 'alter table '||c.owner||'.'||c.table_name||' modify constraint '||c.constraint_name||' disable;' "exec_sql"
from user_constraints c, user_constraints f
where c.r_owner=f.owner
and c.r_constraint_name=f.constraint_name
and f.table_name='FATHER';

exec_sql

-------------------------------------

alter table SCOTT.CHILD modify constraint FK_ID disable;

 

然后执行上面的查询结果,就可以禁掉所有的子表约束,truncate父表就不会报错了。

SQL> alter table SCOTT.CHILD modify constraint FK_ID disable;

Table altered.

SQL> truncate table father;

Table truncated.

当然,此时子表的引用约束不一定能起来(enable),取决于子表是否有数据。

SQL> alter table SCOTT.CHILD modify constraint FK_ID enable;
alter table SCOTT.CHILD modify constraint FK_ID enable
                                          *
ERROR at line 1:
ORA-02298: cannot validate (SCOTT.FK_ID) - parent keys not found

将子表数据全部删除,就可以起来(enable)子表的引用约束。

SQL> truncate table child;

Table truncated.

SQL> alter table SCOTT.CHILD modify constraint FK_ID enable;

Table altered.

 


SQL> drop table father;
drop table father
           *
ERROR at line 1:
ORA-02449: unique/primary keys in table referenced by foreign keys

 

这种情况可以使用cascade constraints子句一同将子表的引用约束删掉。

SQL> drop table father cascade constraints;

Table dropped.

6. 外键列必须创建索引

案例一:外键列无索引,删除主表数据

--主表

CREATE TABLE supplier
    (   supplier_id     number(10)     not null,
        supplier_name     varchar2(50)     not null,
        contact_name     varchar2(50),   
        CONSTRAINT supplier_pk PRIMARY KEY (supplier_id)
    );


INSERT INTO supplier VALUES (1, 'Supplier 1', 'Contact 1');
INSERT INTO supplier VALUES (2, 'Supplier 2', 'Contact 2');
COMMIT;

--子表

CREATE TABLE product
    (   product_id     number(10)     not null,
        product_name    varchar2(50)    not null,
        supplier_id     number(10)     not null,
        CONSTRAINT fk_supplier
         FOREIGN KEY (supplier_id)
         REFERENCES supplier(supplier_id)
         on delete cascade
    ); 
INSERT INTO product VALUES (1, 'Product 1', 1);
INSERT INTO product VALUES (2, 'Product 2', 1);
INSERT INTO product VALUES (3, 'Product 3', 2);
COMMIT;

--在执行下面的操作之前,先查看数据库锁的情况

SQL> select * from v$lock t
  2  where t.TYPE in ('TX','TM')
  3  order by t.SID
  4  /

no rows selected

 

SESSION1(SID=269):

SQL> delete from supplier where supplier_id=1;

1 row deleted.

 

SESSION2(SID=270):

SQL> delete from supplier where supplier_id=2;
(现象hang住了)

 

SESSION3(SID=271):

SQL> insert into supplier values (3,'supplier 3','Contact 3');
(现象hang住了)

--此时再查看锁的情况

SQL> select * from v$lock t
  2  where t.TYPE in ('TX','TM')
  3  order by t.SID
  4  /

ADDR     KADDR           SID TY        ID1        ID2      LMODE    REQUEST      CTIME      BLOCK
-------- -------- ---------- -- ---------- ---------- ---------- ---------- ---------- ----------
34FC7378 34FC7494        269 TX     262156        112          6          0        225          0
34F47858 34F47870        269 TM       9941          0          3          0        225          1
34F477AC 34F477C4        269 TM       9939          0          3          0        225          0

34F47904 34F4791C        270 TM       9939          0          3          0        174          0
34F479B0 34F479C8        270 TM       9941          0          0          5        174          0
34F47B08 34F47B20        271 TM       9941          0          0          2        123          0
34F47A5C 34F47A74        271 TM       9939          0          3          0        123          0

7 rows selected.

可以看到SESSION1(SID=269)获得了一个行锁(type=TX lmode=6),同时两个表锁(type=TM lmode=3);

SESSION2(SID=270)获得了一个表锁(type=TM lmode=3),同时请求一个表锁(type=TM request=5 id1=9941)

SESSION3(SID=271)同样获得了一个表锁(type=TM lmode=3),同时请求一个表锁(type=TM request=2 id1=9941)。

 

--将外键列创建索引,进行上面同样的操作,查看是否有锁等待。

create index ind_product on product (supplier_id) online nologging;

SESSION1(SID=269):

SQL> delete from supplier where supplier_id=1;

1 row deleted.

 

SESSION2(SID=270):

SQL> delete from supplier where supplier_id=2;
1 row deleted.


SESSION3(SID=271):

SQL> insert into supplier values (3,'supplier 3','Contact 3');
1 row created.
发现此时就没有了锁表的情况,查看数据库锁的情况:

SQL> select * from v$lock t
  2  where t.TYPE in ('TX','TM')
  3  order by t.SID
  4  /

ADDR     KADDR           SID TY        ID1        ID2      LMODE    REQUEST      CTIME      BLOCK
-------- -------- ---------- -- ---------- ---------- ---------- ---------- ---------- ----------
34FC7378 34FC7494        269 TX     458771        120          6          0        204          0
34F477AC 34F477C4        269 TM       9939          0          3          0        204          0
34F47858 34F47870        269 TM       9941          0          3          0        204          0
34FC78A4 34FC79C0        270 TX     131076        112          6          0        162          0
34F47904 34F4791C        270 TM       9939          0          3          0        162          0
34F479B0 34F479C8        270 TM       9941          0          3          0        162          0
34F47B08 34F47B20        271 TM       9941          0          2          0         81          0
34FC7DC0 34FC7EDC        271 TX     655361        113          6          0         81          0
34F47A5C 34F47A74        271 TM       9939          0          3          0         81          0

9 rows selected.

说白了,就是外键列必须创建索引。