Yandex Metrika
sanches.free

Иерархия в MySQL: дерево разделов и строковые ключи

Контекст Битрикс

Деревья встречаются в разделах инфоблоков, меню и кастомных категориях. В ядре чаще опираются на API и ORM, но отчёты и миграции иногда требуют «чистого» SQL по таблице вида id, parent_id, name.

Идея старого трюка

Для числовых идентификаторов распространён приём: накопить обойдённый список в пользовательской переменной @pv, на каждой строке дописывать id и проверять FIND_IN_SET(parent_id, @pv). Порядок строк критичен — обычно сортируют по parent_id, id.

SELECT id, name, parent_id
FROM (
  SELECT *
  FROM demo_tree
  ORDER BY parent_id, id
) AS ordered,
     (SELECT @pv := '19') AS seed
WHERE FIND_IN_SET(parent_id, @pv) > 0
  AND @pv := CONCAT(@pv, ',', id);

Почему ломается на UUID

Для CHAR(36) корни проблемы двойные. Во‑первых, CONCAT(@pv, ',', id) в старых версиях MySQL в составе WHERE могло не пересчитываться так, как ожидает автор, если выражение «оптимизируется» до константы. Во‑вторых, подстановки вроде (@pv := …) OR 1 маскируют вычисление и дают неверный объём выборки: оптимизатор может пропустить побочный эффект присваивания.

Надёжнее не полагаться на порядок вычисления пользовательских переменных в одном запросе — поведение не гарантировано стандартом.

Современная альтернатива: рекурсивный CTE (MySQL 8+)

Для потомков от корня и для цепочки предков лучше использовать WITH RECURSIVE: план предсказуем, типы строковых ключей не ломают семантику.

WITH RECURSIVE walk AS (
  SELECT id, name, parent_id, 0 AS lvl
  FROM demo_tree
  WHERE id = 'b10'
  UNION ALL
  SELECT c.id, c.name, c.parent_id, w.lvl + 1
  FROM demo_tree c
  INNER JOIN walk w ON c.parent_id = w.id
)
SELECT * FROM walk ORDER BY lvl, id;

Цепочка «вверх» к корню

Для списка предков удобен второй рекурсивный шаблон: стартуем от листа и поднимаемся по parent_id до корня или пустого значения.

Итог

Трюки с @var оставьте для legacy и коротких скриптов; в продакшене на MySQL 8 и в новых модулях рядом с Битрикс ориентируйтесь на CTE и явные ограничения по глубине, чтобы не упереться в бесконечный цикл при ошибочной связи.

Не хотите копаться сами?

Починю за 1-3 дня. Без предоплаты — оплата по результату.

15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии