Выбор записи из дерева с MySQL - PullRequest
0 голосов
/ 18 января 2011

Примечание: Возможно, вы захотите перейти к нижней части, чтобы прочитать актуальный вопрос, прежде чем читать все эти дополнительные материалы.

Я работаю над реализацией ACL для CakePHP. Главным образом потому, что я пытаюсь отделить его от AuthComponeny, чтобы я мог использовать Authsome для своих проектов. У меня есть теория реализации, но я наткнулся на небольшой камень преткновения.

Очевидно, я хочу свести к минимуму количество запросов к базе данных. Поэтому я спрашиваю здесь о том, что это возможно (я серьезно сомневаюсь, что это так).

Предполагая такую ​​структуру таблицы:

id - int(10), auto_increment, primary_key, not null
parent_id - int(10), null
model - varchar(255), utf8_bin, null
foreign_key - int(10), null
alias - varchar(255), utf8_bin, null,
lft - int(10), null
rght - int(10), null

И несколько записей для проверки (контроллеры - это корневой узел, и я мог бы неправильно указать значения lft и rght):

1, null, null, null, controllers,          1,  14
2, 1,    null, null, one_test_controllers, 2,  7
3, 2,    null, null, one_action,           3,  4
4, 2,    null, null, two_action,           5,  6
5, 1,    null, null, two_test_controllers, 8,  13
6, 5,    null, null, one_action,           9,  10
7, 5,    null, null  two_action,           11, 12

И два тестовых пути:

$test1 = '/controllers/one_test_controller/two_action';
$test2 = '/controllers/two_test_controller/two_action';

Давать эти результаты, возвращая массив идентификаторов от наиболее релевантных до наименее релевантных:

// Result 1
array(
    0 => 4,
    1 => 2,
    2 => 1
)

// Result 2
array(
    0 => 7,
    1 => 5,
    2 => 1
)

То, что я сейчас делаю, это взорвать () путь к массиву и (используя $ test1 для этого примера) сначала найти все записи, которые соответствуют псевдониму "two_action"; затем просматривая результаты и находя все записи, которые соответствуют родительским идентификаторам последнего результата и имеют псевдоним "one_test_controller". Затем повторяйте, пока parent_id = 0.

Это работает, но очевидно, что несколько рекурсивных SQL-запросов не идеальны, есть ли магический SQL-запрос, который может помочь мне в этом? Или я прав, полагая, что это лучшее, что он может получить?

1 Ответ

2 голосов
/ 18 января 2011

а?У вас уже есть структура для извлечения данных путем синтаксического анализа пути за один проход с деревом смежности.

Однако, не сохраняя полный путь / не требуя уникальных имен узлов, вы не сможете искать снизу вверх.Учтите, что в обоих тестовых случаях вы начинаете с 'two_action', но ищете 2 разных листа.Если вы храните полный путь в таблице (или можете ссылаться на узлы по идентификатору из вашего запроса), тогда ....

SELECT ancestors.*
FROM ahier ancestors,
(SELECT lft, rght
  FROM ahier ref
  WHERE ref.path='/controllers/one_test_controller/two_action') ilv
WHERE (ancestors.lft >= ilv.left AND ancestors.rght <= ilv.rght)
ORDER BY ancestors.lft ASC;

или с использованием идентификаторов:

SELECT ancestors.*
FROM ahier ancestors,
(SELECT lft, rght
  FROM ahier ref
  WHERE ref.id=4) ilv
WHERE (ancestors.lft >= ilv.left AND ancestors.rght <= ilv.rght)
ORDER BY ancestors.lft ASC;

В качестве альтернативыВы могли бы написать запрос, чтобы вернуть каждый возможный путь, который имеет определенный псевдоним узла - но это тоже не будет очень эффективным ....

SELECT treenum, ancestors.*
FROM ahier ancestors,
(SELECT lft, rght, id as treenum
  FROM ahier ref
  WHERE ref.alias='two_action') ilv
WHERE (ancestors.lft >= ilv.left AND ancestors.rght <= ilv.rght)
ORDER BY treenum, ancestors.lft ASC;

(и его легко восстановить из lft и rght изthe parent_ids)

...