Работа с вложенными наборами в 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 (), если хотите более подробно.