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.
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
);
Edit:
There is "kind of" an issue with this query when finding infinite loops.
If we have this tree:
b --> c --> d
b <-- c
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 3
So as you can see, the problem is inside the loop, the values from outside will always have its max correct lvl