Вот решение с рекурсивным cte:
with cte (id, parent_id, ids) as
(
select id, parent_id, to_char(id) from mytable
union all
select t.id, t.parent_id, ids || ' -> ' || t.id
from cte
join mytable t on t.id = cte.parent_id
)
cylce id set cycle to 1 default 0
select ids as cycling_ids
from cte
where cycle = 1
order by ids;
Результат:
+ ----------------------+
| CYCLING_IDS |
+ ----------------------+
| 1 -> 4 -> 3 -> 2 -> 1 |
| 2 -> 1 -> 4 -> 3 -> 2 |
| 3 -> 2 -> 1 -> 4 -> 3 |
| 4 -> 3 -> 2 -> 1 -> 4 |
+ ----------------------+
Если вы хотите увидеть каждый цикл только один раз (что я предполагаю), запомните минимум ID на цикл и показывать только один цикл на минимальный ID:
with cte (id, parent_id, ids, min_id) as
(
select id, parent_id, to_char(id), id from mytable
union all
select t.id, t.parent_id, ids || ' -> ' || t.id, least(t.id, cte.min_id)
from cte
join mytable t on t.id = cte.parent_id
)
cycle id set cycle to 1 default 0
select min(ids) as cycling_ids
from cte
where cycle = 1
group by min_id
order by min_id;
Результат:
+ ----------------------+
| CYCLING_IDS |
+ ----------------------+
| 1 -> 4 -> 3 -> 2 -> 1 |
+ ----------------------+
Демонстрация с большим количеством идентификаторов и различными случаями: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=f7f924cd8759d67a188b7c11f2d071ef
(Это все еще не идеально. Если очень маленький идентификатор приводит к более высоким идентификаторам, образующим цикл, например, если мы вставили идентификатор 0, ссылающийся на идентификатор 3 также как родитель, запрос отобразил бы цикл более одного раза. Этого нелегко избежать, так как мы должны были бы обнаружить минимальный идентификатор в круге . Я бы, вероятно, написал небольшую функцию PL / SQL, чтобы получить этот минимальный идентификатор из строки идентификаторов.)