Я решил следовать http://www.artfulsoftware.com/mysqlbook/sampler/mysqled1ch20.html
Итак, теперь я ищу некоторую помощь с кодом.
Я использую их данные для моего тестирования. Итак, я визуализировал дерево так:
array('value' => 'Richard Shakespeare', array('value' => 'Henry', array('value' => 'Joan'), array('value' => 'Margaret'), array('value' => 'William', array('value' => 'Susana', array('value' => 'Elizabeth Hall', array('value' => 'John Bernard'))), array('value' => 'Hamnet'), array('value' => 'Judith', array('value' => 'Shakespeare Quiney'), array('value' => 'Richard Quiney'), array('value' => 'Thomas Quiney'))), array('value' => 'Gilbert'), array('value' => 'Joan', array('value' => 'William Hart'), array('value' => 'Mary Hart'), array('value' => 'Thomas Hart'), array('value' => 'Micheal Hart')), array('value' => 'Anne'), array('value' => 'Richard'), array('value' => 'Edmond')), array('value' => 'John'));
Поэтому, если мы хотим вставить это в базу данных, мы хотим
Array ( [0] => Array ( [value] => Richard Shakespeare [left] => 1 [right] => 46 ) [1] => Array ( [value] => Henry [left] => 2 [right] => 43 ) [2] => Array ( [value] => Joan [left] => 3 [right] => 4 ) [3] => Array ( [value] => Margaret [left] => 5 [right] => 6 ) [4] => Array ( [value] => William [left] => 7 [right] => 24 ) [5] => Array ( [value] => Susana [left] => 8 [right] => 13 ) [6] => Array ( [value] => Elizabeth Hall [left] => 9 [right] => 12 ) [7] => Array ( [value] => John Bernard [left] => 10 [right] => 11 ) [8] => Array ( [value] => Hamnet [left] => 14 [right] => 15 ) [9] => Array ( [value] => Judith [left] => 16 [right] => 23 ) [10] => Array ( [value] => Shakespeare Quiney [left] => 17 [right] => 18 ) [11] => Array ( [value] => Richard Quiney [left] => 19 [right] => 20 ) [12] => Array ( [value] => Thomas Quiney [left] => 21 [right] => 22 ) [13] => Array ( [value] => Gilbert [left] => 25 [right] => 26 ) [14] => Array ( [value] => Joan [left] => 27 [right] => 36 ) [15] => Array ( [value] => William Hart [left] => 28 [right] => 29 ) [16] => Array ( [value] => Mary Hart [left] => 30 [right] => 31 ) [17] => Array ( [value] => Thomas Hart [left] => 32 [right] => 33 ) [18] => Array ( [value] => Micheal Hart [left] => 34 [right] => 35 ) [19] => Array ( [value] => Anne [left] => 37 [right] => 38 ) [20] => Array ( [value] => Richard [left] => 39 [right] => 40 ) [21] => Array ( [value] => Edmond [left] => 41 [right] => 42 ) [22] => Array ( [value] => John [left] => 44 [right] => 45 ) )
Поэтому на ум приходит вопрос, как лучше всего это сделать?
Мое решение было:
$container = array(); function children($item){ $children = 0; foreach($item as $node) if(is_array($node)) $children += children($node)+1; return $children; } function calculate($item, &$container, $data = array(0,0)){ //althought this one is actually of no use, it could be useful as it contains a count $data[0]++; //$left $right = ($data[0]+(children($item)*2))+1; //store the values in the passed container $container[] = array( 'value' => $item['value'], 'left' => $data[0], 'right' => $right, ); //continue looping $level = $data[1]++; foreach($item as &$node) if(is_array($node)) $data = calculate($node, $container, $data); $data[1] = $level; $data[0]++; return $data; } calculate($tree, $container);
Насколько это эффективно, я не знаю.
Но теперь на запросы.
Чтобы выбрать всех потомков узла, мы можем использовать
SELECT child.value AS 'Descendants of William', COUNT(*) AS `Level` FROM tester AS parent JOIN tester AS child ON child.`left` BETWEEN parent.`left` AND parent.`right` WHERE parent.`left` > 7 AND parent.`right` < 24 GROUP BY child.value ORDER BY `level`;
Чтобы выбрать всех потомков узла, на определенную глубину, мы можем использовать
Обратите внимание, что мы выбираем потомков Уильяма на глубину 2
Уильямс вышел: 7, Уильямс право: 24, Уровни: 2
SELECT child.value AS 'Descendants of William', COUNT(*) AS `Level` FROM tester AS parent JOIN tester AS child ON child.`left` BETWEEN parent.`left` AND parent.`right` WHERE parent.`left` > 7 AND parent.`right` < 24 GROUP BY child.value HAVING `level` <= 2 ORDER BY `level`;
Так что это достаточно легко.
Но теперь я хочу знать несколько вещей,
Обратите внимание, что в реальной базе данных, а также влево / вправо все строки имеют уникальный идентификатор и «родительский» столбец, содержащий идентификатор их приглашенных, или нулевые, если не приглашены
David
в детство из Judith
, как мне это сделать? Mary Hart's
и родителя родителей ( array('Henery', 'Joan', 'Mary Hart')
). Как мне это сделать? William Hart
из Joan
Как я это делаю? Для обновления / удаления вам необходимо увеличить / уменьшить значения left
/ right
для всех элементов ветви.
Примеры запросов, которые вы можете найти здесь .
Насколько это эффективно, я не знаю.
Вложенные наборы работают ОЧЕНЬ медленно с большими деревьями при обновлении / вставке / удалении. И очень быстро выбрать.
Поэтому используйте эту модель только со статическими данными, которые будут храниться без изменений большую часть времени, и это дерево не будет содержать тысячи узлов (или какое-либо обновление займет минуты). Материализованный путь работает намного быстрее.
для получения родительского узла вам понадобятся узлы с left_id <child.left_id и right_id> child.right_id, если вы хотите, чтобы прямой предк выбирал один из предыдущего набора с самым высоким значением left_id.
для удаления узла удалите его, а затем опустите дважды все идентификаторы слева / справа, которые больше, чем идентификатор удаленных элементов. if (leftId> deleted.leftId) leftId- = 2 одинаково для rightId
для вставки узла сделайте некоторое пространство для него, добавив + 2 ко всем узлам с leftId> parent.rightId, затем parent.rightId + = 2, затем вставьте узел с leftId = parent.rightId-2 и rightId = parent.rightId-1
Все ваши вопросы могут быть решены очень легко, если вы используете DFS для каждой взаимосвязи, а затем снова используете функцию calculate (), если хотите более подробно.