2011-01-18 2 views
0

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

Я работаю над реализацией 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 

И несколько записей для тестирования (контроллеры является корневой узел, и я мог бы получить ЯПТ и 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-запрос, который может мне помочь? Или я прав, полагая, что это лучшее, что он может получить?

ответ

2

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

Однако, не сохраняя полный путь/требуя уникальных имен узлов, вы не можете искать снизу вверх. Подумайте - в обоих тестовых случаях вы начинаете с «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 от parent_ids)

+0

Спасибо, я соглашусь с этим. К сожалению, я не могу изменить таблицу. Он должен быть совместим со схемой, сгенерированной скриптом оболочки CakePHPs. – rich97

Смежные вопросы