Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Oracle SQL looped self-join

Context:
Let's say that I have table that has a FOREIGN KEY which references its own PRIMARY KEY, like this:

|---------------------|------------------|------------------|
|          ID         |       NAME       |     PARENT_ID    |
|---------------------|------------------|------------------|
|          01         |       John       |         04       |
|---------------------|------------------|------------------|
|          02         |       Paul       |         01       |
|---------------------|------------------|------------------|
|          03         |       George     |         02       |
|---------------------|------------------|------------------|
|          04         |       Ringo      |         03       |
|---------------------|------------------|------------------|


Problem:
So as you see there is looped hierarchy: Ringo->George->Paul->John->Ringo->George->Paul->John->etc.

Question:
Whether there is a SQL select that can detect such loops?

I know that I can write recursive PL/SQL procedure but I prefer solution with "pure" SQL.

Thank you in advance

like image 991
michal.d Avatar asked Jul 03 '26 18:07

michal.d


2 Answers

You can use a CONNECT BY query with the CONNECT_BY_ISCYCLE pseudo-column to look for loops - see example from Oracle docs:

SELECT last_name "Employee", CONNECT_BY_ISCYCLE "Cycle"
FROM employees
WHERE level <= 3 
AND   department_id = 80
START WITH last_name = 'King'
CONNECT BY NOCYCLE PRIOR employee_id = manager_id;
like image 125
Tony Andrews Avatar answered Jul 05 '26 12:07

Tony Andrews


You can do this with connect by nocycle and connect_by_iscycle. For your table structure, that would look like:

select id, name, parent_id, connect_by_iscycle
from mytable
connect by nocycle id = prior parent_id
start with id = 4

connect by nocycle causes the query to stop iterating when a cyle is met, and pseudo-column connect_by_iscycle contains a flag that indicates at which point it happened, as shown in this demo:

ID | NAME   | PARENT_ID | CONNECT_BY_ISCYCLE
-: | :----- | --------: | -----------------:
 4 | Ringo  |         3 |                  0
 3 | George |         2 |                  0
 2 | Paul   |         1 |                  0
 1 | John   |         4 |                  1  --> cycle detected here
like image 21
GMB Avatar answered Jul 05 '26 13:07

GMB