oracle 禁用/启用引用的FK

x3naxklr  于 2023-05-16  发布在  Oracle
关注(0)|答案(1)|浏览(104)

我正试图自动化一个过程,以禁用和启用外键,这样我就可以删除“坏数据”。
下面是我们的一个应用程序模式的示例。我发现这个Tom Kyte查询可以显示一些表之间的关系。

create table c ( x NUMBER, 
CONSTRAINT c_pk PRIMARY KEY(x)
);

create table c1 ( x NUMBER, 
CONSTRAINT c1_pk PRIMARY KEY(x),
CONSTRAINT c1_fk FOREIGN KEY (x) REFERENCES c(x));

create table c2 ( x NUMBER, 
CONSTRAINT c2_pk PRIMARY KEY(x),
CONSTRAINT c2_fk FOREIGN KEY (x) REFERENCES c2(x));

create table c3 ( x NUMBER, 
CONSTRAINT c3_pk PRIMARY KEY(x),
CONSTRAINT c3_fk FOREIGN KEY (x) REFERENCES c2(x));

create table c4 ( x NUMBER, 
CONSTRAINT c4_pk PRIMARY KEY(x),
CONSTRAINT c4_fk FOREIGN KEY (x) REFERENCES c2(x));

 /* child/parent relationship */
with cte as (
  select table_name, constraint_type, constraint_name, r_constraint_name,
    max(decode(constraint_type,'R',1,0)) over(partition by table_name) is_r
  from user_constraints 
  where constraint_type in ('P', 'U', 'R')
)
, child_parent as (
  select distinct s.table_name child, d.table_name parent, d.constraint_type
  from (select * from cte where constraint_type = 'R' or is_r = 0) s
  left join cte d
    on s.r_constraint_name = d.constraint_name
    and s.table_name != d.table_name
)
select level lvl, child, parent, constraint_type
from child_parent
start with parent is null
connect by parent = prior child
order siblings by parent, child;

LVL CHILD   PARENT  CONSTRAINT_TYPE
1   C    -   - 
2   C1  C   P
1   C2   -   - 
2   C3  C2  P
2   C4  C2  P

我有以下代码,这将禁用和启用FK的。我知道这可以合并成一段代码(过程,它接受参数???)但我还没有达到这一点。
正如你从下面的输出中看到的,它禁用和启用了模式中的所有FK,我只需要代码对一组引用执行此操作。
例如,如果我传入表“C”,我的代码应该只禁用和启用FK C1_FK。如果传入表“C2”,则代码应仅禁用和启用C3_FK、C4_FK
任何帮助将不胜感激。提前感谢所有回答的人。注意,如果有更好的方法来编码这个解决方案,我愿意接受所有的建议

/* disable foreign keys */

DECLARE 
  sql_command        varchar2(500);
BEGIN
    FOR r IN (
        SELECT '"' || a.owner
                    || '"."'
                    || a.table_name
                    || '"' AS full_table_name,
                a.constraint_name
            FROM user_constraints a
            JOIN user_constraints b
                ON ( a.r_constraint_name = b.constraint_name
                    AND a.r_owner = b.owner )
            WHERE a.constraint_type = 'R'
                AND a.status = 'ENABLED'
            ORDER BY 1 )  
    LOOP    
        sql_command := 'alter table ' || r.full_table_name || ' disable constraint ' || r.constraint_name;
                       dbms_output.put_line(sql_command);
     execute immediate sql_command;      
    END LOOP ;
END ;
/

Statement processed.
alter table "XXX"."C1" disable constraint C1_FK
alter table "XXX"."C2" disable constraint C2_FK
alter table "XXX"."C3" disable constraint C3_FK
alter table "XXX"."C4" disable constraint C4_FK

DECLARE 
  sql_command        varchar2(500);
BEGIN
    FOR r IN (
        SELECT '"' || a.owner
                    || '"."'
                    || a.table_name
                    || '"' AS full_table_name,
                a.constraint_name
            FROM user_constraints a
            JOIN user_constraints b
                ON ( a.r_constraint_name = b.constraint_name
                    AND a.r_owner = b.owner )
            WHERE a.constraint_type = 'R'
                AND a.status = 'DISABLED'
            ORDER BY 1 )  
    LOOP    
        sql_command := 'alter table ' || r.full_table_name || ' enable constraint ' || r.constraint_name;
                       dbms_output.put_line(sql_command);
     execute immediate sql_command;      
    END LOOP ;
END ;
/

Statement processed.
alter table "XXX"."C1" enable constraint C1_FK
alter table "XXX"."C2" enable constraint C2_FK
alter table "XXX"."C3" enable constraint C3_FK
alter table "XXX"."C4" enable constraint
5jdjgkvh

5jdjgkvh1#

如果你试图从父表中删除行,你不能禁用和重新启用它的外键。通常,子表中仍会有指向父行(现在已丢失)的行。
所以要么你需要

  • 同时从子表中删除/将其FK列设置为null
  • 使用novalidate选项重新启用外键

如果您试图从父级中删除的是“坏数据”,那么您几乎肯定也希望从子级中删除它。在这种情况下,您还需要对这些子表运行删除/更新。
这使得启用/禁用FK的过程是相当浪费的工作。只要在表中从子表到父表运行DML,它就“正常工作”,同时启用FK。
如果你想简化这个过程,你可以将外键声明为on delete [cascade | set null]。这会自动将所有删除从父表传播到子表。

设置on delete cascade时要非常小心。有了这个,在一个小的查找表上运行一个delete就很容易了,最终会清除大量的数据!

例如:

create table par1 ( c1 int constraint p1_pk primary key );
create table par2 ( c2 int constraint p2_pk primary key );

create table chld (
  c1 
    constraint chld_fk_1 references par1 
    on delete cascade,
  c2 
    constraint chld_fk_2 references par2
    on delete set null
);

insert into par1 values ( 1 );
insert into par1 values ( 2 );
insert into par2 values ( 1 );
insert into par2 values ( 2 );

insert into chld values ( 1, 1 );
insert into chld values ( 2, 2 );

select * from chld;
/*
        C1         C2
---------- ----------
         1          1
         2          2
*/

commit;

delete par1 where c1 = 1;
delete par2 where c2 = 2;

select * from chld;
/*
        C1         C2
---------- ----------
         2 <null>    
*/

相关问题