Работа с вложенными наборами в mysql?
я решил следовать 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`;
так что это легко достаточно.
но теперь я хочу знать несколько вещей,
обратите внимание, что в фактической базе данных, а также слева/справа все строки имеют уникальный идентификатор и столбец "родитель", содержащий их идентификатор приглашенных, или null, если не приглашен
- допустим, я хочу вставить
David
как дитяJudith
, как мне это сделать? - допустим, я хочу сделать
Mary Hart's
родитель и родители родителя (array('Henery', 'Joan', 'Mary Hart')
), как мне это сделать? - допустим я хотите удалить
William Hart
СJoan
как я это делаю?
3 ответов
для обновления / удаления вам нужно будет увеличить / уменьшить left
/right
значения всех элементов ветви.
Примеры запросов, которые вы можете найти здесь.
насколько это эффективно, я не знаю.
вложенные наборы работают очень медленно с большими деревьями при обновлении / вставке / удалении. И очень быстро выбрать.
Поэтому использовать эту модель только со статическими данными, которые будут храниться без изменений большую часть времени, и это дерево не будет содержать тысячи узлов (или любое обновление займет несколько минут). Материализованный путь работает намного быстрее.
чтобы получить родителей узла, вам нужны узлы с left_id ребенка.right_id, если вы хотите только прямого предка, выберите один из предыдущего набора с самым высоким left_id.
чтобы удалить узел, удалите его, а затем дважды опустите все левые / правые идентификаторы, которые больше, чем удаленный элемент right id. if (leftId > deleted.leftId) leftId - =2 то же самое для rightId
вставить узел освободить место для этого добавляем+2 ко всем узлам с leftId > parent.rightId то из родителей.rightId += 2 затем вставьте узел с leftId = parent.rightId-2 и rightId=родитель.rightId-1
все ваши вопросы могут быть решены очень легко, если вы используете DFS для каждого отношения, а затем снова используете свою функцию calculate (), если хотите более подробно.