Если в базе есть хоть какой-то намек на иерархию – parent_id, children_ids и так далее – вам не избежать рекурсивных запросов. Если точнее, избежать их можно: выбрать данные и обойти в приложении. Но зачем, если это может сделать база?

Рекурсивный запрос на самом деле не имеет отношение к рекурсии. Это цикл или свертка. В целом он выглядит так:

with recursive NAME as (
  init-part
  UNION (ALL)
  continuous-part
)
select * from NAME

Часть init-part выполняется однажды. Часть continuous-part выполняется много раз, при этом псевдоним NAME ссылается на результат прошлой выборки. Например, когда continuous-part сработает в первый раз, в NAME будет то, что вернула init-part. Когда continuous-part сработает второй раз, в NAME будет то, что вернула continuous-part до этого и так далее.

Цикл заканчивается, когда в очередной раз continuous-part вернула пустой результат. Должно быть ясное условие остановки, иначе запрос уйдет в бесконечный цикл. В итоговом SELECT алиас NAME содержит результат всех операций.

При помощи рекурсивного запроса легко обойти дерево потомков, графы, отношения, различные связи. Более того – внутри рекурсивного запроса доступны формы SEARCH DEPTH FIRST и SEARCH BREADTH FIRST, выявление циклов по массиву вершин и другие приятные вещи. Так что когда на собеседовании скажут обойти граф, расчехляйте psql!

Последнее, кстати, не такая уж и шутка. Скажем, дали вам файл с вершинами. А откуда взялся файл? Скорее всего, из базы. “Вот где графы храните, там и обходите.” (с) Поэтому связные структуры я обхожу в SQL без выгрузки в приложение. Это и быстрее, и результат виден сразу, и удобно коллегам-аналитикам, которые не знают Кложу/Питон.