Построение таблицы зависимостей с помощью рекурсивного запроса - PullRequest
4 голосов
/ 18 сентября 2008

Я пытаюсь построить граф зависимостей таблиц на основе внешних ключей между ними. Этот график должен начинаться с произвольного имени таблицы в качестве корня. Я мог бы, учитывая имя таблицы, просмотреть таблицы, которые ссылаются на нее, используя представление all_constraints, затем просмотреть таблицы, которые ссылаются на них, и так далее, но это было бы ужасно неэффективно. Я написал рекурсивный запрос, который делает это для всех таблиц, но когда я добавляю:

START WITH Table_Name=:tablename

Он не возвращает все дерево.

Ответы [ 2 ]

8 голосов
/ 18 сентября 2008
    select parent, child, level from (
select parent_table.table_name parent, child_table.table_name child
 from user_tables      parent_table,
      user_constraints parent_constraint,
      user_constraints child_constraint,
      user_tables      child_table
where parent_table.table_name = parent_constraint.table_name
  and parent_constraint.constraint_type IN( 'P', 'U' )
  and child_constraint.r_constraint_name = parent_constraint.constraint_name
  and child_constraint.constraint_type   = 'R'
  and child_table.table_name = child_constraint.table_name
  and child_table.table_name != parent_table.table_name
)
start with parent = 'DEPT'
connect by prior child = parent

должно сработать (конечно, замените имя таблицы), предполагая, что все в одной схеме. Используйте версии DBA_ таблиц словаря данных и условия для столбцов OWNER и R_OWNER, если вам нужно обрабатывать зависимости между схемами. При дальнейшем рассмотрении это не учитывает само-ссылочные ограничения (то есть ограничение на таблицу EMP, к которой столбец MGR ссылается на столбец EMPNO), поэтому вам придется изменить код для обработки этого случая, если вам нужно иметь дело с самореференциальными ограничениями.

В целях тестирования я добавил несколько новых таблиц в схему SCOTT, которые также ссылаются на таблицу DEPT (включая зависимость от внуков)

SQL> create table dept_child2 (
  2  deptno number references dept( deptno )
  3  );

Table created.

SQL> create table dept_child3 (
  2    dept_child3_no number primary key,
  3    deptno number references dept( deptno )
  4  );

Table created.

SQL> create table dept_grandchild (
  2    dept_child3_no number references dept_child3( dept_child3_no )
  3  );

Table created.

и подтвердил, что запрос вернул ожидаемый результат

SQL> ed
Wrote file afiedt.buf

  1  select parent, child, level from (
  2  select parent_table.table_name parent, child_table.table_name child
  3   from user_tables      parent_table,
  4        user_constraints parent_constraint,
  5        user_constraints child_constraint,
  6        user_tables      child_table
  7  where parent_table.table_name = parent_constraint.table_name
  8    and parent_constraint.constraint_type IN( 'P', 'U' )
  9    and child_constraint.r_constraint_name = parent_constraint.constraint_name
 10    and child_constraint.constraint_type   = 'R'
 11    and child_table.table_name = child_constraint.table_name
 12    and child_table.table_name != parent_table.table_name
 13  )
 14  start with parent = 'DEPT'
 15* connect by prior child = parent
SQL> /

PARENT                         CHILD                               LEVEL
------------------------------ ------------------------------ ----------
DEPT                           DEPT_CHILD3                             1
DEPT_CHILD3                    DEPT_GRANDCHILD                         2
DEPT                           DEPT_CHILD2                             1
DEPT                           EMP                                     1
2 голосов
/ 18 сентября 2008

Самый простой способ сделать это - скопировать всю информацию FK в простую таблицу из 2 столбцов (родительский, дочерний), а затем использовать следующий алгоритм:

while (rows left in that table)
  list = rows where table name exists in child but not in parent
  print list
  remove list from rows

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

P.S. Убедитесь, что вы не вставляете самоссылающиеся таблицы в начальный список (child = parent)

...