Дерево категорий одним запросом
Вопрос
Как бы ты хранил дерево категорий и как одним запросом получить всю ветку под выбранной категорией?
Попробуйте сами
Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.
Показать ответ и разбор
Коротко
Что должно прозвучать в ответе
Простейшее хранение: adjacency list, колонка parent_id
Ветка через WITH RECURSIVE: якорь (стартовая категория) UNION ALL шаг (дети найденных)
Глубину считать счётчиком в рекурсии, путь собирать массивом
Защита от циклов: проверка, что id ещё нет в пути, или ограничение глубины
Альтернативы: materialized path (ltree), nested sets, closure table и их цена при изменениях
Индекс на parent_id обязателен
Что могут спросить следом
- Как вывести путь от корня до узла?
- Что выберешь, если дерево читают в 1000 раз чаще, чем меняют?
Разбор
По умолчанию храню как adjacency list: у категории есть parent_id. Это просто и дёшево менять. Всю ветку достаю рекурсивным CTE: якорь выбирает стартовую категорию, рекурсивная часть присоединяет детей уже найденных, и так пока новых строк нет. Заодно считаю уровень (lvl + 1) и собираю путь массивом path || id, по нему удобно и сортировать, и ловить циклы: WHERE NOT e.id = ANY(path). Нужен индекс на parent_id, иначе каждый шаг рекурсии будет полным сканом. Если дерево огромное и читается гораздо чаще, чем меняется, можно взять materialized path через расширение ltree: поддерево выбирается одним условием по индексу. Или closure table, где хранятся все пары предок-потомок. Платим сложностью переноса веток.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.
Термины по теме
Ещё по теме «Подзапросы и CTE»
- Полный путь категории в дереве
- Клиенты, у которых все заказы оплачены
- Дни без продаж по регионам
- Цепочка руководителей вверх по дереву
- Дороже любой книги: ALL против MAX
- Категория с максимальной выручкой
- Два последних заказа каждого клиента через LATERAL
- Дороже среднего по своей категории