Рекурсивный запрос для табличных зависимостей не рекурсирует не так сильно, как хотелось бы

У меня возникла идея написать запрос для поиска всех дочерних таблиц корневой таблицы на основе внешних ключей.

Запрос выглядит так:

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_1, CHILD_TAB_2 или CHILD_TAB_3.

Рекурсивные запросы для меня новы, поэтому я предполагаю, что что-то упустил в предложении connect by, но здесь я ничего не понимаю. Действительно ли возможно получить полную иерархию ROOT_TAB в одном запросе, или мне лучше обернуть запрос в рекурсивную процедуру?


person FrustratedWithFormsDesigner    schedule 04.02.2010    source источник
comment
Почему вы откатили мою правку?   -  person Air    schedule 05.04.2017
comment
@Air Некоторые части мне понравились, а другие нет. Я не видел способа выборочно откатить только часть, поэтому просто откатил все. Почему вы вообще внесли это редактирование? Что в этом такого неправильного? ;)   -  person FrustratedWithFormsDesigner    schedule 05.04.2017
comment
Я объяснил это в резюме редактирования. Вы не должны ставить перед заголовком тег (см. эту публикацию часто задаваемых вопросов MSE для некоторое обсуждение того, почему), в вашем сообщении было несколько орфографических ошибок, и я чувствовал, что могу сделать его более читабельным. Способ сделать выборочный откат — просто внести новое редактирование с желаемыми реверсами.   -  person Air    schedule 05.04.2017
comment
@Air: Да, я, вероятно, сделаю новое редактирование с моими любимыми правками позже ...   -  person FrustratedWithFormsDesigner    schedule 05.04.2017


Ответы (3)


Вы хотите что-то вроде этого:

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. Если в нескольких схемах есть таблицы с одинаковыми именами, объединение только по имени_таблицы даст неправильные результаты.

person Marcel Wolf    schedule 04.02.2010
comment
Да, кажется, это помогает. Я только что нашел немного другой запрос, который делает то же самое: stackoverflow.com/questions/87877/ - person FrustratedWithFormsDesigner; 05.02.2010

В случае с несколькими схемами и несколькими корневыми таблицами попробуйте что-то вроде:

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
person unbob    schedule 29.06.2012

После глубокого исследования я сделал свою собственную версию, которая обрабатывает все таблицы и извлекает максимальный уровень таблицы в иерархии (она читает все схемы, принимая также во внимание таблицы без отношений родитель-потомок, которые будут на уровне 1 вместе с корнем те). Если у вас есть доступ, используйте таблицы dba_ вместо all_.

      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

Итак, как вы можете видеть, проблема внутри цикла, значения извне всегда будут иметь свой максимальный правильный уровень.

person guritaburongo    schedule 29.04.2017