表依赖项的递归查询不会像我想的那样递归

时间:2010-02-04 22:20:45

标签: oracle recursion oracle10g recursive-query

我有一个想法,我可以根据外键编写查询来查找根表的所有后代表。

查询如下所示:

select level, lpad(' ', 2 * (level - 1)) || uc.table_name as "TABLE", uc.constraint_name, uc.r_constraint_name
from all_constraints uc
where uc.constraint_type in ('R', 'P')
start with uc.table_name = 'ROOT_TAB'
connect by nocycle prior uc.constraint_name = uc.r_constraint_name
order by level asc;

结果我看起来像这样:

        1   ROOT_TAB        XPKROOTTAB  
        1   ROOT_TAB        R_20           XPKPART_TAB
        2     CHILD_TAB_1   R_40           XPKROOTTAB
        2     CHILD_TAB_2   R_115          XPKROOTTAB
        2     CHILD_TAB_3   R_50           XPKROOTTAB

此结果是ROOT_TAB的所有子表,但查询不会递归到CHILD_TAB_1CHILD_TAB_2CHILD_TAB_3的子项。

递归查询对我来说是新的,所以我猜我在connect by子句中遗漏了一些东西,但我在这里画了一个空白。实际上是否可以在单个查询中获得ROOT_TAB的完整层次结构,或者我最好在递归过程中包装查询?

3 个答案:

答案 0 :(得分:7)

你想要这样的东西:

select t.table_name, level,lpad(' ', 2 * (level - 1))||t.table_name 
from user_tables t
join user_constraints c1 
    on (t.table_name = c1.table_name 
    and c1.constraint_type in ('U', 'P'))
left join user_constraints c2 
    on (t.table_name = c2.table_name 
    and c2.constraint_type='R')
start with t.table_name = 'ROOT_TAB'
connect by prior c1.constraint_name = c2.r_constraint_name

原始查询的问题是子表的uc.constraint_name是外键的名称。这对于将第一个子节点连接到根表是很好的,但是不需要将第二个节点上的子节点连接到第一个节点。这就是为什么你需要连接两次约束 - 一次得到表的主键,一次得到外键。

顺便说一句,如果您要查询all_ *视图而不是user_ *视图,通常需要将它们连接到table_name AND owner,而不仅仅是table_name。如果多个模式具有相同名称的表,则仅加入table_name将得到不正确的结果。

答案 1 :(得分:3)

对于具有多个模式和多个根表的情况,请尝试类似:

WITH constraining_tables AS (SELECT owner, constraint_name, table_name
                               FROM all_constraints
                              WHERE owner LIKE 'ZZZ%' AND constraint_type IN ('U', 'P')),
     constrained_tables AS (SELECT owner, constraint_name, table_name, r_owner, r_constraint_name
                              FROM all_constraints
                             WHERE owner LIKE 'ZZZ%' AND constraint_type = 'R'),
     root_tables AS (SELECT owner, table_name FROM constraining_tables
                     MINUS
                     SELECT owner, table_name FROM constrained_tables)
    SELECT c1.owner || '.' || c1.table_name, LEVEL, LPAD (' ', 2 * (LEVEL - 1)) || c1.owner || '.' || c1.table_name
      FROM    constraining_tables c1
           LEFT JOIN
              constrained_tables c2
           ON c1.owner = c2.owner AND c1.table_name = c2.table_name
START WITH c1.owner || '.' || c1.table_name IN (SELECT owner || '.' || table_name FROM root_tables)
CONNECT BY PRIOR c1.constraint_name = c2.r_constraint_name

答案 2 :(得分:0)

经过深入调查后,我创建了自己的版本来处理所有表格,并在层次结构中检索表格的最大级别(它读取所有模式,同时考虑到没有父子关系的表格,这将是1级与根的一起)。如果您有权访问,请使用dba_ tables而不是all_1。

      WITH hier AS (
                         SELECT child_table owner_table_name
                              , LEVEL lvl
                              , LPAD (' ', 4 * (LEVEL - 1)) || child_table indented_child_table
                              , sys_connect_by_path( child_table, '|' )  tree
                           FROM (
/*----------------------------------------------------------------------*/
/* Retrieve all tables. Set them as the Child column, and set their     */
/* Parent Column to NULL. This is the root list (first iteration)       */
/*----------------------------------------------------------------------*/
                                  SELECT NULL                              parent_table
                                       , a.owner || '.' || a.table_name    child_table
                                    FROM all_tables a
                                   UNION
/*----------------------------------------------------------------------*/
/* List of all possible Parent-Child relations. This table is used as   */
/* a link list, to link the current iteration with the next one, from   */
/* root to last child (last child is what we are interested to find).   */
/*----------------------------------------------------------------------*/
                                  SELECT p.owner   || '.' || p.table_name            parent_table
                                       , c.owner   || '.' || c.table_name            child_table
                                    FROM all_constraints p, all_constraints c
                                   WHERE p.owner || '.' || p.constraint_name = c.r_owner || '.' || c.r_constraint_name
                                     AND (p.constraint_type = 'P' OR p.constraint_type = 'U')
                                     AND c.constraint_type = 'R'
                                )
                     START WITH parent_table IS NULL
/*----------------------------------------------------------------------*/
/* NOCYCLE prevents infinite loops (i.e. self referencing table constr) */
/*----------------------------------------------------------------------*/
                     CONNECT BY NOCYCLE PRIOR child_table = parent_table
                   )
                     SELECT *
                       FROM hier
                      WHERE (owner_table_name, lvl) IN (   SELECT owner_table_name
                                                                , MAX(lvl)
                                                             FROM hier
                                                         GROUP BY owner_table_name
                                                       );

编辑: 在查找无限循环时,此查询存在“种类”问题。

如果我们有这棵树:

b --> c --> d
b <-- c

它会将lvl 2分配给c:b --> c             并且lvl 2到b为:c --> b

对于d,它将检测到b --> c --> d 所以它会分配lvl 3

正如你所看到的,问题出在循环内部,来自外部的值总是最大的正确lvl