I had an idea that I could write a query to find all the descendent tables of a root table, based on foreign keys.
Query looks like this:
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;
The results I get look like this:
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
This result is all the child tables of ROOT_TAB
, but the query does not recurse to the children of CHILD_TAB_1
, CHILD_TAB_2
, or CHILD_TAB_3
.
Recursive queries are new to me so I'm guessing I'm missing something in the connect by
clause, but I'm drawing a blank here. Is it actually possible to get the full hierarchy of ROOT_TAB
in a single query, or am I better off wrapping the query in a recursive procedure?
For the case with multiple schemas and multiple root tables, try something like:
After deep deep investigation, I made my own version that processes all tables and retreives the table's max level in hierarchy (it reads all schemas, taking also into account the tables with no parent-child relationship, that will be at level 1 along with root ones). If you have access, use dba_ tables instead of all_ ones.
Edit: There is "kind of" an issue with this query when finding infinite loops.
If we have this tree:
it will assign lvl 2 to c as:
b --> c
and lvl 2 to b as:c --> b
for d, it will detect
b --> c --> d
so it will assign lvl 3So as you can see, the problem is inside the loop, the values from outside will always have its max correct lvl
You want something like this:
The problem with the original query is that uc.constraint_name for the child table is the name of the foreign key. That is fine for connecting the first child to the root table, but it is not what you need to connect the children on the second level to the first. That is why you need to join against the constraints twice -- once to get the table's primary key, once to get the foreign keys.
As an aside, if you are going to be querying the all_* views rather than the user_* views, you generally want to join them on table_name AND owner, not just table_name. If multiple schemas have tables with the same name, joining on just table_name will give incorrect results.