Cycle detection is a built-in feature of recursive CTEs: demo at db<>fiddle
CYCLEclausesclause informs it which value indicates a loop if it repeats- It's followed by a
booleanfield that it'll populate, populated for the records with loops and, then an array field that holds the whole path/group with the cycle. - Since the third field
pathaccumulates all keys, it can grow quickly on larger and more complex sets. As an optimised, fast and memory-efficient alternative, considerpgrouting.
WITH recursive tmp AS ( SELECT supervisor_id, emp_id FROM incoming_records UNION ALL SELECT tmp.supervisor_id, t.emp_id FROM tmp INNER JOIN incoming_records AS t ON t.supervisor_id = tmp.emp_id )CYCLE emp_id SET is_cycle USING path ----- here SELECT * FROM tmp WHERE is_cycle; | supervisor_id | emp_id | is_cycle | path |
|---|---|---|---|
| rt14 | rt14 | t | {(rt14),(rt14)} |
| rt15 | rt16 | t | {(rt16),(rt15),(rt16)} |
| rt16 | rt15 | t | {(rt15),(rt16),(rt15)} |
| rt17 | rt18 | t | {(rt18),(rt19),(rt17),(rt18)} |
| rt18 | rt19 | t | {(rt19),(rt17),(rt18),(rt19)} |
| rt19 | rt17 | t | {(rt17),(rt18),(rt19),(rt17)} |
| rt20 | rt21 | t | {(rt21),(rt22),(rt23),(rt20),(rt21)} |
| rt21 | rt22 | t | {(rt22),(rt23),(rt20),(rt21),(rt22)} |
| rt22 | rt23 | t | {(rt23),(rt20),(rt21),(rt22),(rt23)} |
| rt23 | rt20 | t | {(rt20),(rt21),(rt22),(rt23),(rt20)} |
If you want to avoid them rather than select only those, switch to WHERE NOT is_cycle;.
The SQL-standard CYCLE clause for built-in cycle detection was added in v14.