Глубоко вложенные подзапросы для обхода деревьев в MySQL

У меня есть таблица в моей базе данных, где я храню древовидную структуру, используя модель гибридного вложенного набора (MPTT) (которая имеет lft а также rght значения) и модель списка смежности (хранение parent_id на каждом узле).

my_table (id, parent_id, lft, rght, alias)

Этот вопрос не относится ни к одному из аспектов дерева MPTT, но я подумал, что оставлю его на всякий случай, если у кого-то будет хорошее представление о том, как его использовать.

Я хочу преобразовать путь псевдонимов для конкретного узла. Например: "users.admins.nickf" найдет узел с псевдонимом "nickf", который является дочерним по отношению к псевдониму "admin", который является дочерним по отношению к "users", который находится в корне. Существует уникальный индекс на (parent_id, alias),

Я начал с написания функции, чтобы она разделяла путь к ее частям, а затем опрашивал базу данных один за другим:

SELECT `id` FROM `my_table` WHERE `parent_id` IS NULL AND `alias` = 'users';-- 1
SELECT `id` FROM `my_table` WHERE `parent_id` = 1 AND `alias` = 'admins';   -- 8
SELECT `id` FROM `my_table` WHERE `parent_id` = 8 AND `alias` = 'nickf';    -- 37

Но потом я понял, что могу сделать это с помощью одного запроса, используя переменное количество вложений:

SELECT `id` FROM `my_table` WHERE `parent_id` = (
    SELECT `id` FROM `my_table` WHERE `parent_id` = (
        SELECT `id` FROM `my_table`
        WHERE `parent_id` IS NULL AND `alias` = 'users'
    ) AND `alias`  = 'admins'
) AND `alias` = 'nickf';

Поскольку количество подзапросов зависит от количества шагов в пути, я столкнусь с проблемами, связанными с наличием слишком большого количества подзапросов? (Если есть даже такая вещь)

Есть ли лучшие / более умные способы выполнить этот запрос?

2 ответа

Решение

Это работает?

select r0.id 
  from my_table as r0 
  join my_table as r1 on(r0.parent_id = r1.id) 
  join my_table as r2 on(r1.parent_id = r2.id)
 where r0.alias='nickf'
   and r1.alias='admins'
   and r2.alias='users'
   and r2.parent_id is null

Мне кажется, на самом деле нет необходимости во вложенных подзапросах..

или я не прав, что-то упустил?

Я сам задавался вопросом об этом, и искал что-то, что не становилось медленнее, когда вы углублялись (имеется в виду большее количество уровней в обоих вариантах выше). Проблема, с которой я столкнулся в "моей версии", состоит в том, что она должна создавать все возможные пути. прежде чем он сузит результат до того, который вы на самом деле ищете... так что я думаю, что версия lexu должна превзойти мою даже для очень большого вложения, потому что это простое объединение, но я надеюсь, что кто-то может увидеть его и пожелать расширить на это дальше.

Кроме того, этот способ сделать это определенно выиграл бы от хранимого процесса и / или просмотра его части "путей" (без предложения HAVING). Возможно, для них это лучшее решение, но я, к сожалению, пока недостаточно знаю о производительности SQL, чтобы сказать наверняка. Я могу сказать, что мой становится медленнее, так как данные (число возможных комбинаций путей) увеличиваются, но с представлением (поскольку результат кэшируется и используется для его сужения), оно кажется быстрым (самый большой набор данных, который я нашел было 370 всего, в какой-то момент я создам гораздо больший набор для тестирования.)

SELECT node.id, GROUP_CONCAT(parent.alias
                 ORDER BY parent.lft SEPARATOR '.') AS path_name
FROM my_table AS node, my_table AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rght
GROUP BY node.id HAVING path_name = 'users.admins.nickf'
Другие вопросы по тегам