我该如何在Oracle SQL Developer中查找引用特定表的表?

230
Oracle SQL Developer 中,若我正在查看一张表的信息,则可以查看其约束条件,这样便可查看到外键(从而知道哪些表被该表引用),并且可以查看依赖关系以了解哪些包或其他内容引用该表。但我不确定如何查找哪些表引用了这个表。
例如,假设我正在查看 emp 表,有另一张名为emp_dept 的表记录员工在哪个部门工作,并通过 emp_id 引用了 emp 表的主键。是否可以通过程序中的某个UI元素(而非SQL)找到它引用了 emp 表,而无需我知道 emp_dept 表的存在?

为了澄清这里所询问的引用方向:这个问题是从 emp 表的角度来询问指向 emp 的内部引用(即来自其他表的引用)。如果您想要另一个方向(即 TableName 从自身向其他表进行的外部引用),我认为这个问题已经在这个其他问题中得到了回答。 - cellepo
这里是一个实际的仅限UI答案(OP在此问题中实际请求)。另外,这里是[到目前为止] 最简单的SQL答案 - cellepo
12个回答

292

不可以。Oracle SQL Developer没有这样的选项。

你需要手动执行一个查询或使用其他工具(例如PLSQL Developer有此选项)。下面是 PLSQL Developer 使用的 SQL:

select table_name, constraint_name, status, owner
from all_constraints
where r_owner = :r_owner
and constraint_type = 'R'
and r_constraint_name in
 (
   select constraint_name from all_constraints
   where constraint_type in ('P', 'U')
   and table_name = :r_table_name
   and owner = :r_owner
 )
order by table_name, constraint_name

r_owner 是模式名称,r_table_name 是你要查找引用的表名称。名称区分大小写。


请注意,在 Oracle SQL Developer 的报告选项卡中,有一个选项“所有表/依赖项”,这来自于ALL_DEPENDENCIES,它指的是“当前用户可访问的过程、包、函数、包体和触发器之间的依赖关系,包括对没有任何数据库链接创建的视图的依赖关系”。因此,该报告对你的问题没有价值。


39
谢谢你的回答。对于 Oracle Sql Developer 的表现感到遗憾。 - Greg
1
你提到了PLSQL Developer可以执行这个功能,你能解释一下如何做吗? - Nicholas
4
在对象浏览器中,选择一个表,右键点击该表,然后选择“外键引用”。 - FerranB
3
这个回答提到SQL Developer 4.1及以上版本现在有一个选项在“Model”标签下,可以以ERD格式显示此信息。 - SnoringFrog
1
r_owner是您正在使用的模式,r_table_name是您要查找引用的表。 - Flowy
如何让这个查询变快?在我的数据库上需要大约3秒钟。 - René Winkler

117

将此项添加到SQL Developer作为扩展程序,请按照以下步骤进行:

  1. 将下面的代码保存到一个XML文件中(例如fk_ref.xml):
<items>
    <item type="editor" node="TableNode" vertical="true">
    <title><![CDATA[FK References]]></title>
    <query>
        <sql>
            <![CDATA[select a.owner,
                            a.table_name,
                            a.constraint_name,
                            a.status
                     from   all_constraints a
                     where  a.constraint_type = 'R'
                            and exists(
                               select 1
                               from   all_constraints
                               where  constraint_name=a.r_constraint_name
                                      and constraint_type in ('P', 'U')
                                      and table_name = :OBJECT_NAME
                                      and owner = :OBJECT_OWNER)
                               order by table_name, constraint_name]]>
        </sql>
    </query>
    </item>
</items>
  1. 在 SQL Developer 中添加扩展:

    • 工具 > 首选项
    • 数据库 > 用户定义的扩展
    • 单击“添加行”按钮
    • 在类型中选择“EDITOR”,位置是您保存上面xml文件的地方
    • 单击“确定”,然后重新启动SQL Developer
  2. 导航到任何表格,你现在应该看到一个额外的标签,标为FK References,它显示新的 FK 信息。

  3. 参考


你知道“Packages”的节点名称是什么吗?我在网上找到的所有xsd链接都已失效(即Oracle已将它们删除)。 - James Sumners
1
我在您的建议上做了一个小改变:*and owner = user*在order by之前,这样如果您有两个架构中相同表的两个实例,则只获取与您的架构相关的引用。 - user1708042
我在 and exists 之前添加了这个条件:and owner = :OBJECT_OWNER - M Denis
3
@M-Denis,在这种情况下,您可能会错过来自其他模式的参考。 - Youw
应用此后,在 Oracle SQL Developer VM 上运行 describe books;select * from books;,它不会显示 fk 引用选项卡。 - mLstudent33

37

在下面的查询中,将[Your TABLE] 替换为 emp

select owner,constraint_name,constraint_type,table_name,r_owner,r_constraint_name
  from all_constraints 
 where constraint_type='R'
   and r_constraint_name in (select constraint_name 
                               from all_constraints 
                              where constraint_type in ('P','U') 
                                and table_name='[YOUR TABLE]');

我认为 constraint_type in ('P','U') 是多余的,因为如果约束 TOTO 的 constraint_type 是 'R',那么 TOTO 的 r_constraint_name 当然是引用表中类型为 'P' 或 'U' 的约束的名称。没有必要指定它。你正在使用一个 IN,所以它就像许多 OR,我们只关心 OR 的唯一操作数,它评估为 true。 - Gab是好人

13

您可以从ALL_CONSTRAINTS视图中查询此信息:

SELECT table_name
FROM ALL_CONSTRAINTS
WHERE constraint_type = 'R' -- "Referential integrity"
  AND r_constraint_name IN
    ( SELECT constraint_name
      FROM ALL_CONSTRAINTS
      WHERE table_name = 'EMP'
        AND constraint_type IN ('U', 'P') -- "Unique" or "Primary key"
    );

1
外键可以引用唯一键,而不仅仅是主键。此外,表名可以在多个模式中使用,这将导致多个匹配项。如果您要使用“所有约束”而不是“用户约束”,则还需要使用“Owner”列。 - Mark Roddy
1
感谢您对“R”、“U”和“P”的注释。 - Jeff
不要忘记在 SQL 请求的末尾加上分号。 - Gab是好人
顺便提一下,constraint_type在('P','U')中是多余的,因为如果约束TOTO的constraint_type是'R',那么TOTO的r_constraint_name当然是引用表中类型为'P'或'U'的约束的名称。没有必要指定它。 - Gab是好人

10

SQL Developer 4.1于2015年5月发布,增加了一个模型选项卡,以实体关系图的形式显示引用您表的外键。


2
如果你需要在脚本中使用,这可能不是很有用,但如果你只是想了解连接方面的知识,这似乎是现代化的方式。 - SnoringFrog
1
@SnoringFrog 嗯,从技术上讲,问题要求一个用户界面元素,所以这是最合适的答案。 - WhatsThePoint

9
这个功能已经存在多年了,但在2011年还未被引入产品中。
只需点击“模型”页面即可。
确保您至少使用了4.0版本(发布于2013年)才能访问此功能。 输入图像描述

谢谢。这对我很有帮助。 - SuRa

6
SELECT DISTINCT table_name, 
                constraint_name, 
                column_name, 
                r_table_name, 
                position, 
                constraint_type 
FROM   (SELECT uc.table_name, 
               uc.constraint_name, 
               cols.column_name, 
               (SELECT table_name 
                FROM   user_constraints 
                WHERE  constraint_name = uc.r_constraint_name) r_table_name, 
               (SELECT column_name 
                FROM   user_cons_columns 
                WHERE  constraint_name = uc.r_constraint_name 
                       AND position = cols.position)           r_column_name, 
               cols.position, 
               uc.constraint_type 
        FROM   user_constraints uc 
               inner join user_cons_columns cols 
                       ON uc.constraint_name = cols.constraint_name 
        WHERE  constraint_type != 'C') 
START WITH table_name = '&&tableName' 
           AND column_name = '&&columnName' 
CONNECT BY NOCYCLE PRIOR table_name = r_table_name 
                         AND PRIOR column_name = r_column_name; 

这非常有用 - 递归地显示了从某个根表开始,所有包含您在此根表中选择的列值作为键的表。太棒了,谢谢。 - Ev0oD
那真的很酷 - 干得好。我只会加lower()来比较table_name和column_name。 - Tobias Otto
如果我的团队在数据库层面上实际使用了外键,这对我来说也将非常有用。唉,回到看Hibernate代码的路上了。 - Sridhar Sarnobat

4
像这样怎么样:
SELECT c.constraint_name, c.constraint_type, c2.constraint_name, c2.constraint_type, c2.table_name
  FROM dba_constraints c JOIN dba_constraints c2 ON (c.r_constraint_name = c2.constraint_name)
 WHERE c.table_name = <TABLE_OF_INTEREST>
   AND c.constraint_TYPE = 'R';

2
当我将表名从dba_constraints更改为all_constraints时,这对我起作用了,就像这样:SELECT c.constraint_name, c.constraint_type, c2.constraint_name, c2.constraint_type, c2.table_name FROM all_constraints c JOIN all_constraints c2 ON (c.r_constraint_name = c2.constraint_name) WHERE c.table_name = '<TABLE_OF_INTEREST>' AND c.constraint_TYPE = 'R'; - chrisjleu

3

只需将table_name替换为您的主表名称(在OP问题的情况下,应该是emp

select *
from all_constraints
where r_constraint_name in (
    select constraint_name
    from all_constraints
    where table_name='table_name'
); 

除非我弄错了,尽管这个答案没有给出归属,但这个答案似乎是从这个其他答案上类似的问题中复制过来的。虽然我认为那里的答案更适合放在这里,所以它被复制到这里很好(而且它似乎是最简单的解决方案)。 - cellepo

0

在下面替换MY_OWNER_NAMEMY_TABLE_NAME,你就可以递归地准备好了:

DECLARE
FUNCTION list_all_child_tables_and_constraints(asked_table_name in VARCHAR2, parent_table_name in VARCHAR2)
RETURN VARCHAR2 IS
    current_path VARCHAR2(100);
BEGIN
    FOR item IN
    (SELECT fk.TABLE_NAME, constraint_parent.FK FK1, constraint_child.FK FK2
        FROM all_constraints fk, all_constraints pk,
            (SELECT acc.CONSTRAINT_NAME, LISTAGG(acc.COLUMN_NAME, ', ') WITHIN GROUP (ORDER BY acc.COLUMN_NAME) AS FK
                FROM ALL_CONS_COLUMNS acc
                WHERE acc.OWNER = 'MY_OWNER_NAME'
                GROUP BY acc.CONSTRAINT_NAME) constraint_parent,
            (SELECT acc.CONSTRAINT_NAME, LISTAGG(acc.COLUMN_NAME, ', ') WITHIN GROUP (ORDER BY acc.COLUMN_NAME) AS FK
                FROM ALL_CONS_COLUMNS acc
                WHERE acc.OWNER = 'MY_OWNER_NAME'
                GROUP BY acc.CONSTRAINT_NAME) constraint_child
        WHERE pk.owner = fk.r_owner
            AND pk.constraint_name = fk.r_constraint_name
            AND fk.constraint_type = 'R'
            AND pk.table_name = asked_table_name
            AND constraint_parent.CONSTRAINT_NAME = fk.CONSTRAINT_NAME
            AND constraint_child.CONSTRAINT_NAME = fk.R_CONSTRAINT_NAME
            AND pk.owner = 'MY_OWNER_NAME'
            AND fk.owner = 'MY_OWNER_NAME')
    LOOP
        current_path := parent_table_name || ' // ' || item.TABLE_NAME;
        DBMS_OUTPUT.PUT_LINE(current_path);
        DBMS_OUTPUT.PUT_LINE('     [' || item.FK1 || ']  [' || item.FK2 || ']');
        DBMS_OUTPUT.PUT_LINE('');
        current_path := list_all_child_tables_and_constraints(item.TABLE_NAME, current_path);

    END LOOP;
    RETURN '-----------FINISHED-----------';

EXCEPTION
    WHEN OTHERS THEN
        RETURN '-----------FINISHED-----------';
END list_all_child_tables_and_constraints;
BEGIN
DBMS_OUTPUT.PUT_LINE(list_all_child_tables_and_constraints('MY_TABLE_NAME', ''));
END;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接