如何查找指向 Oracle 中一条记录的外键依赖关系?

发布于 2024-08-26 19:05:54 字数 244 浏览 4 评论 0原文

我有一个非常大的 Oracle 数据库,有很多表和数百万行。我需要删除其中之一,但希望确保删除它不会破坏任何其他指向它作为外键记录的依赖行。有没有办法获取指向该行的所有其他记录的列表,或者至少是表模式?我知道我可以尝试自己删除它并捕获异常,但我不会自己运行脚本并需要它第一次运行干净。

我可以使用 Oracle 的 SQL Developer 工具和 AllRoundAutomations 的 PL/SQL Developer。

提前致谢!

I have a very large Oracle database, with many many tables and millions of rows. I need to delete one of them, but want to make sure that dropping it will not break any other dependent rows that point to it as a foreign key record. Is there a way to get a list of all the other records, or at least table schemas, that point to this row? I know that I could just try to delete it myself, and catch the exception, but I won't be running the script myself and need it to run clean the first time through.

I have the tools SQL Developer from Oracle, and PL/SQL Developer from AllRoundAutomations at my disposal.

Thanks in advance!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

多彩岁月 2024-09-02 19:05:54

这是我列出对表的所有引用的解决方案:

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'
;

使用此解决方案,您还可以获得哪个表的哪一列引用目标表的哪一列的信息(并且您可以对其进行过滤)。

Here is my solution to list all references to a table:

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'
;

With this solution you also have the information of which column of which table is referencing which column of your target table (and you can filter on it).

千年*琉璃梦 2024-09-02 19:05:54

我总是查看起始表的外键并返回。数据库工具通常具有依赖项或约束节点。我知道 PL/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

I always look at the Foreign keys for the starting table and work my way back. The DB tools usually have a dependencies or constraints node. I know PL/SQL Developer has a way to see FK's, but it's been a while since I have used it, so I can't explain it...

just replace XXXXXXXXXXXX with a table name...

/* 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
生生漫 2024-09-02 19:05:54

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

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
...

I had a similar problem recently, but experienced soon, that finding the direct dependencies is not enough. So I wrote a query to show a tree of multilevel foreign key dependencies:

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';

It gives a result like this, when using SHIPMENT as MYTABLE in my database:

SHIPMENT <-- ADDRESS
SHIPMENT <-- PACKING_LIST
    PACKING_LIST <-- PACKING_LIST_DETAILS
    PACKING_LIST <-- PACKING_UNIT
        PACKING_UNIT <-- PACKING_LIST_ITEM
    PACKING_LIST <-- PO_PACKING_LIST
...
给妤﹃绝世温柔 2024-09-02 19:05:54

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

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>

这个示例有点作弊。目标主键的名称是硬编码的,并且引用列在所有依赖表上具有相同的名称。解决这些问题留给读者作为练习;)

We can use the data dictionary to identify the tables which reference the primary key of the table in question. From that we can generate some dynamic SQL to query those tables for the value we want to 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>

This example cheats a bit. The name of the target primary key is hardcoded, and the referencing column has the same name on all the dependent tables. Fixing these issues is left as an exercise for the reader ;)

涙—继续流 2024-09-02 19:05:54

也有过类似的情况。就我而言,我有几条记录,它们最终具有相同的 ID,只是大小写不同。想要检查每个记录存在哪些依赖记录,以了解哪个记录最容易删除/更新

以下内容打印出每个子表指向给定记录的所有子记录,以及每个表/主记录组合的计数

declare
  --
  -- Finds and prints out how many children there are per table and value for each value of a given field
  --

  -- Name of the table to base the query on  
  cTable      constant varchar2(20) := 'FOO';
  -- Name of the column to base the query on
  cCol        constant varchar2(10) := 'ID';
  -- Cursor to find interesting values (e.g. duplicates) in master table
  cursor cVals is
    select id 
    from foo f
    where exists (  select 1 from foo f2 
                    where upper(f.id) = upper(f2.id)
                    and f.rowid != f2.rowid );

  -- Everything below here should just work
  vNum        number(18,0);
  vSql        varchar2(4000);

  cOutColSize   number(2,0) := 30;

  cursor cReferencingTables is
    select
      consChild.table_name,
      consChild.constraint_name,
      colChild.column_name     
    from user_constraints consMast
    inner join user_constraints consChild on consMast.constraint_name = consChild.r_constraint_name  
    inner join USER_CONS_COLUMNS colChild on consChild.CONSTRAINT_NAME = colChild.CONSTRAINT_NAME
    inner join USER_CONS_COLUMNS colMast on colMast.CONSTRAINT_NAME = consMast.CONSTRAINT_NAME
    where consChild.constraint_type = 'R'      
      and consMast.table_name = cTable
      and colMast.column_name = cCol
    order by consMast.table_name, consChild.table_name;
begin

  dbms_output.put_line(
    rpad('Table', cOutColSize) || 
    rpad('Column', cOutColSize) || 
    rpad('Value', cOutColSize) || 
    rpad('Number', cOutColSize)
  );
  for rRef in cReferencingTables loop
    for rVals in cVals loop
      vSql := 'select count(1) from ' || rRef.table_name || ' where ' || rRef.column_name || ' = ''' || rVals.id || '''';
      execute immediate vSql into vNum;
      if vNum > 0 then
        dbms_output.put_line(
          rpad(rRef.table_name, cOutColSize) || 
          rpad(rRef.column_name, cOutColSize) || 
          rpad(rVals.id, cOutColSize) || 
          rpad(vNum, cOutColSize) );
      end if;
    end loop;
  end loop;
end;

Had a similar situation. In my case I had a couple of records which had ended up with the same ID differing only by case. Wanted to check what dependent records exists for each to know which was easiest to delete/update

The following prints out all child records pointing to the given record, per child table with a count for each table/master record combination

declare
  --
  -- Finds and prints out how many children there are per table and value for each value of a given field
  --

  -- Name of the table to base the query on  
  cTable      constant varchar2(20) := 'FOO';
  -- Name of the column to base the query on
  cCol        constant varchar2(10) := 'ID';
  -- Cursor to find interesting values (e.g. duplicates) in master table
  cursor cVals is
    select id 
    from foo f
    where exists (  select 1 from foo f2 
                    where upper(f.id) = upper(f2.id)
                    and f.rowid != f2.rowid );

  -- Everything below here should just work
  vNum        number(18,0);
  vSql        varchar2(4000);

  cOutColSize   number(2,0) := 30;

  cursor cReferencingTables is
    select
      consChild.table_name,
      consChild.constraint_name,
      colChild.column_name     
    from user_constraints consMast
    inner join user_constraints consChild on consMast.constraint_name = consChild.r_constraint_name  
    inner join USER_CONS_COLUMNS colChild on consChild.CONSTRAINT_NAME = colChild.CONSTRAINT_NAME
    inner join USER_CONS_COLUMNS colMast on colMast.CONSTRAINT_NAME = consMast.CONSTRAINT_NAME
    where consChild.constraint_type = 'R'      
      and consMast.table_name = cTable
      and colMast.column_name = cCol
    order by consMast.table_name, consChild.table_name;
begin

  dbms_output.put_line(
    rpad('Table', cOutColSize) || 
    rpad('Column', cOutColSize) || 
    rpad('Value', cOutColSize) || 
    rpad('Number', cOutColSize)
  );
  for rRef in cReferencingTables loop
    for rVals in cVals loop
      vSql := 'select count(1) from ' || rRef.table_name || ' where ' || rRef.column_name || ' = ''' || rVals.id || '''';
      execute immediate vSql into vNum;
      if vNum > 0 then
        dbms_output.put_line(
          rpad(rRef.table_name, cOutColSize) || 
          rpad(rRef.column_name, cOutColSize) || 
          rpad(rVals.id, cOutColSize) || 
          rpad(vNum, cOutColSize) );
      end if;
    end loop;
  end loop;
end;
蓝颜夕 2024-09-02 19:05:54
 select c.owner, a.table_name, a.column_name, a.constraint_name, 
       c.r_owner as ref_owner, cpk.table_name as ref_table, 
       cpk.constraint_name as ref_pk
from all_cons_columns a 
join all_constraints c on a.owner = c.owner
    and a.constraint_name = c.constraint_name
 join all_constraints cpk on c.r_owner = cpk.owner
    and c.r_constraint_name = cpk.constraint_name
where c.constraint_type = 'r' and c.table_name= 'table_name';
 select c.owner, a.table_name, a.column_name, a.constraint_name, 
       c.r_owner as ref_owner, cpk.table_name as ref_table, 
       cpk.constraint_name as ref_pk
from all_cons_columns a 
join all_constraints c on a.owner = c.owner
    and a.constraint_name = c.constraint_name
 join all_constraints cpk on c.r_owner = cpk.owner
    and c.r_constraint_name = cpk.constraint_name
where c.constraint_type = 'r' and c.table_name= 'table_name';
凡间太子 2024-09-02 19:05:54

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

   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;

I was surprised at how difficult it was to find the dependency order of tables based on foreign key relationships. I needed it because I wanted to delete the data from all tables and import it again. Here is the query I wrote to list the tables in dependency order. I was able to script the deletes using the query below, and import again using the results of the query in reverse order.

   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;
我是男神闪亮亮 2024-09-02 19:05:54

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;

Oracle constraints uses Table Indexes to reference data.
To find out what tables are referencing one table, just look for index in reverse order.

/* 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;

Obs.: Disabling references improves considerably the time of DML commands (update, delete and insert).

This can help a lot in bulk operations, where you know that all data is consistent.

/* 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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文