Построение графа зависимости таблицы с помощью рекурсивного запроса
-
01-07-2019 - |
Вопрос
Я пытаюсь построить график зависимостей таблиц на основе внешних ключей между ними.Этот граф должен начинаться с произвольного имени таблицы в качестве корня.Я мог бы, зная имя таблицы, искать таблицы, которые ссылаются на нее, используя представление all_constraints, затем искать таблицы, которые ссылаются на них, и так далее, но это было бы ужасно неэффективно.Я написал рекурсивный запрос, который делает это для всех таблиц, но когда я добавляю:
START WITH Table_Name=:tablename
Он не возвращает все дерево.
Решение
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
Другие советы
Самый простой способ сделать это — скопировать всю информацию FK в простую таблицу из двух столбцов (родительский, дочерний), а затем использовать следующий алгоритм:
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).