2010-03-24 56 views
17

我有一个非常大的Oracle数据库,许多表和数百万行。我需要删除其中的一个,但要确保删除它不会破坏指向它作为外键记录的任何其他相关行。有没有办法获得指向这一行的所有其他记录或至少表格模式的列表?我知道我可以尝试自己删除它,并捕获异常,但我不会自己运行该脚本,并需要它在第一次运行时运行清理。如何在Oracle中查找指向一条记录的外键依赖关系?

我有Oracle提供的工具SQL Developer和AllRoundAutomations提供的PL/SQL Developer。

在此先感谢!

+0

将不会尝试删除引用的记录抛出异常? – Andrey 2010-03-24 16:43:11

+0

@Andrey是的,但我需要它运行而不会抛出异常。 – daveslab 2010-03-24 16:49:57

回答

23

我总是看起始表的外键,然后回去工作。数据库工具通常具有依赖关系或约束节点。我知道L/SQL Developer有办法看到FK的,但它已经有一段时间,因为我已经使用过,所以我无法解释它...

只是一个表名替换XXXXXXXXXXXX ...

/* The following query lists all relationships */ 

select 
a.owner||'.'||a.table_name "Referenced Table" 
,b.owner||'.'||b.table_name "Referenced by" 
,b.constraint_name "Foreign Key" 
from all_constraints a, all_constraints b 
where 
b.constraint_type = 'R' 
and a.constraint_name = b.r_constraint_name 
and b.table_name='XXXXXXXXXXXX' -- Table name 
order by a.owner||'.'||a.table_name 
+1

Sql是区分大小写的 – eschneider 2010-03-24 17:59:22

+0

你可以把这个查询放在'code'模式下吗,所以它更容易阅读?否则,我现在正在尝试。 – daveslab 2010-03-24 18:00:41

+0

使用SQL选择您的响应部分,然后按或按下按钮直到编辑器工具栏中的直接右侧,引号为其符号。确保将其分解成多行。 – daveslab 2010-03-24 18:05:47

3

我们可以使用数据字典来标识引用相关表的主键的表。由此我们可以生成一些动态SQL来查询这些表以获得我们想要的zap值:

SQL> declare 
    2  n pls_integer; 
    3  tot pls_integer := 0; 
    4 begin 
    5  for lrec in (select table_name from user_constraints 
    6     where r_constraint_name = 'T23_PK') 
    7  loop 
    8   execute immediate 'select count(*) from '||lrec.table_name 
    9        ||' where col2 = :1' into n using &&target_val; 
10   if n = 0 then 
11    dbms_output.put_line('No impact on '||lrec.table_name); 
12   else 
13    dbms_output.put_line('Uh oh! '||lrec.table_name||' has '||n||' hits!'); 
14   end if; 
15   tot := tot + n; 
16  end loop; 
17  if tot = 0 
18  then 
19   delete from t23 where col2 = &&target_val; 
20   dbms_output.put_line('row deleted!'); 
21  else 
22   dbms_output.put_line('delete aborted!'); 
23  end if; 
24 end; 
25/
Enter value for target_val: 6 
No impact on T34 
Uh oh! T42 has 2 hits! 
No impact on T69 
delete aborted! 

PL/SQL procedure successfully completed. 

SQL> 

这个例子作弊了一下。目标主键的名称是硬编码的,并且引用列在所有从属表上具有相同的名称。解决这些问题,就留给读者做练习;)

23

这里是我的解决办法列出一个表的所有引用:

select 
    src_cc.owner as src_owner, 
    src_cc.table_name as src_table, 
    src_cc.column_name as src_column, 
    dest_cc.owner as dest_owner, 
    dest_cc.table_name as dest_table, 
    dest_cc.column_name as dest_column, 
    c.constraint_name 
from 
    all_constraints c 
inner join all_cons_columns dest_cc on 
    c.r_constraint_name = dest_cc.constraint_name 
    and c.r_owner = dest_cc.owner 
inner join all_cons_columns src_cc on 
    c.constraint_name = src_cc.constraint_name 
    and c.owner = src_cc.owner 
where 
    c.constraint_type = 'R' 
    and dest_cc.owner = 'MY_TARGET_SCHEMA' 
    and dest_cc.table_name = 'MY_TARGET_TABLE' 
    --and dest_cc.column_name = 'MY_OPTIONNAL_TARGET_COLUMN' 
; 

有了这个解决方案也有其列的表中的信息正在引用目标表的哪一列(并且可以对其进行过滤)。

5

我最近有类似的问题,但很快就会遇到,找到直接依赖关系是不够的。所以我写了一个查询,以显示多外键的依赖关系树:

SELECT LPAD(' ',4*(LEVEL-1)) || table1 || ' <-- ' || table2 tables, table2_fkey 
FROM 
    (SELECT a.table_name table1, b.table_name table2, b.constraint_name table2_fkey 
    FROM user_constraints a, user_constraints b 
    WHERE a.constraint_type IN('P', 'U') 
    AND b.constraint_type = 'R' 
    AND a.constraint_name = b.r_constraint_name 
    AND a.table_name != b.table_name 
    AND b.table_name <> 'MYTABLE') 
CONNECT BY PRIOR table2 = table1 AND LEVEL <= 5 
START WITH table1 = 'MYTABLE'; 

它提供了这样的结果,在我的数据库使用SHIPMENT为MYTABLE时:

SHIPMENT <-- ADDRESS 
SHIPMENT <-- PACKING_LIST 
    PACKING_LIST <-- PACKING_LIST_DETAILS 
    PACKING_LIST <-- PACKING_UNIT 
     PACKING_UNIT <-- PACKING_LIST_ITEM 
    PACKING_LIST <-- PO_PACKING_LIST 
... 
0

我当时是多么惊讶很难找到基于外键关系的表的依赖性顺序。我需要它,因为我想从所有表中删除数据并再次导入它。这是我写的查询以依赖性顺序列出的查询。我能够使用下面的查询编写删除脚本,并以相反的顺序使用查询结果重新导入。

SELECT referenced_table 
     ,MAX(lvl) for_deleting 
     ,MIN(lvl) for_inserting 
    FROM 
     (-- Hierarchy of dependencies 
     SELECT LEVEL lvl 
       ,t.table_name referenced_table 
       ,b.table_name referenced_by 
     FROM user_constraints A 
     JOIN user_constraints b 
       ON A.constraint_name = b.r_constraint_name 
       and b.constraint_type = 'R' 
     RIGHT JOIN user_tables t 
       ON t.table_name = A.table_name 
     START WITH b.table_name IS NULL 
     CONNECT BY b.table_name = PRIOR t.table_name 
     ) 
    GROUP BY referenced_table 
    ORDER BY for_deleting, for_inserting; 
0

Oracle约束使用表索引来引用数据。
要找出哪些表引用了一个表,只需按相反顺序查找索引。

/* Toggle ENABLED and DISABLE status for any referencing constraint: */ 

select 'ALTER TABLE '||b.owner||'.'||b.table_name||' '|| 
     decode(b.status, 'ENABLED', 'DISABLE ', 'ENABLE ')|| 
     'CONSTRAINT '||b.constraint_name||';' 
    from all_indexes a, 
     all_constraints b 
where a.table_name='XXXXXXXXXXXX' -- Table name 
    and a.index_name = b.r_constraint_name; 

实验值:禁用引用大大提高DML的时间命令(更新,删除和插入)。

这可以帮助大量批量操作,你知道所有的数据是一致的。

/* List which columns are referenced in each constraint */ 

select ' TABLE "'||b.owner||'.'||b.table_name||'"'|| 
     '('||listagg (c.column_name, ',') within group (order by c.column_name)||')'|| 
     ' FK "'||b.constraint_name||'" -> '||a.table_name|| 
     ' INDEX "'||a.index_name||'"' 
     "REFERENCES" 
    from all_indexes a, 
     all_constraints b, 
     all_cons_columns c 
where rtrim(a.table_name) like 'XXXXXXXXXXXX' -- Table name 
    and a.index_name = b.r_constraint_name 
    and c.constraint_name = b.constraint_name 
group by b.owner, b.table_name, b.constraint_name, a.table_name, a.index_name 
order by 1;