Vous voulez quelque chose comme ça :
select t.table_name, level,lpad(' ', 2 * (level - 1))||t.table_name
from user_tables t
join user_constraints c1
on (t.table_name = c1.table_name
and c1.constraint_type in ('U', 'P'))
left join user_constraints c2
on (t.table_name = c2.table_name
and c2.constraint_type='R')
start with t.table_name = 'ROOT_TAB'
connect by prior c1.constraint_name = c2.r_constraint_name
Le problème avec la requête d'origine est que uc.constraint_name pour la table enfant est le nom de la clé étrangère. C'est bien pour connecter le premier enfant à la table racine, mais ce n'est pas ce dont vous avez besoin pour connecter les enfants du deuxième niveau au premier. C'est pourquoi vous devez joindre deux fois les contraintes :une fois pour obtenir la clé primaire de la table, une fois pour obtenir les clés étrangères.
Soit dit en passant, si vous allez interroger les vues all_* plutôt que les vues user_*, vous souhaitez généralement les joindre sur table_name AND owner, pas seulement table_name. Si plusieurs schémas ont des tables portant le même nom, joindre uniquement table_name donnera des résultats incorrects.