Construindo um gráfico de dependência tabela com um recursivo consulta
-
01-07-2019 - |
Pergunta
Eu estou tentando construir um gráfico de dependência de tabelas com base nas chaves estrangeiras entre eles. Este gráfico tem de começar com um nome de tabela arbitrária como sua raiz. Eu poderia, dado um olhar nome da tabela se as tabelas que fazem referência a ele usando os all_constraints vista, em seguida, procurar as tabelas que referenciá-los, e assim por diante, mas isso seria horrível ineficiente. Eu escrevi uma consulta recursiva que faz isso para todas as tabelas, mas quando eu adiciono:
START WITH Table_Name=:tablename
Não devolve a árvore inteira.
Solução
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
deve funcionar (substitua o nome da tabela, é claro), assumindo que tudo está no mesmo esquema. Use as versões DBA_ das tabelas de dicionário de dados e as condições para as colunas proprietário ea R_OWNER se você precisa lidar com dependências cross-esquema. Em uma reflexão mais aprofundada, este não leva em conta as restrições auto-referenciais (ou seja, uma restrição na tabela EMP que as referências de coluna MGR A coluna NUM_EMP) ou, então você teria que modificar o código para lidar com esse caso, se você precisa lidar com restrições auto-referenciais.
Para fins de teste, eu adicionei algumas novas tabelas para o esquema SCOTT que também referência tabela DEPT (incluindo uma dependência neto)
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.
e verificou-se que a consulta retornou a saída esperada
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
Outras dicas
Mais simples maneira de fazer isso é copiar todas as informações FK em uma simples, 2-coluna (pais, filhos) mesa, e, em seguida, usar o seguinte algoritmo:
while (rows left in that table)
list = rows where table name exists in child but not in parent
print list
remove list from rows
isso é tudo. Basicamente, você primeiro imprimir e remover todos os nós que não dependem de qualquer coisa. Depois que está sendo feito, alguns outros nós vai ficar livre e você pode repetir o processo.
P.S. Certifique-se de não inserir tabelas de auto-referência na lista inicial (criança = pai)