Работа с вложенными множествами в MySQL? - PullRequest
7 голосов
/ 10 июня 2011

Я решил следовать 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`;

Так что это достаточно просто.

Но теперь я хочуЧтобы знать несколько вещей,
Обратите внимание, что в реальной базе данных, а также влево / вправо все строки имеют уникальный идентификатор, а также «родительский» столбец, содержащий их приглашающегоs id или null, если не приглашен

  • Допустим, я хочу вставить David как ребенок Judith, как мне это сделать?
  • Допустим, я хочу получить Mary Hart's Родитель и Родительский родитель (array('Henery', 'Joan', 'Mary Hart')), Как мне это сделать?
  • Допустим, я хочу удалить William Hart из Joan Как мне это сделать?

Ответы [ 3 ]

1 голос
/ 10 июня 2011

Для обновления / удаления вам нужно будет увеличить / уменьшить left / right значения всех элементов ветви.
Примеры запросов вы можете найти здесь .

Насколько это эффективно, я не знаю.

Вложенные множества работают ОЧЕНЬ медленно с большими деревьями при обновлении / вставке / удалении. И очень быстро, чтобы выбрать.
Поэтому используйте эту модель только со статическими данными, которые большую часть времени будут храниться без изменений, и это дерево не будет содержать тысячи узлов (или любое обновление займет несколько минут). Материализованный путь работает намного быстрее.

1 голос
/ 10 июня 2011
  • , чтобы получить родителей узла, вам нужны узлы с left_id child.right_id, если вы хотите, чтобы прямой предок выбрал только один из предыдущих наборов с наибольшим значением left_id.

  • чтобы удалить узел, удалите его, а затем уменьшите вдвое все левые / правые идентификаторы, которые больше, чем правый идентификатор удаленного элемента. if (leftId> удаленный.leftId) leftId- = 2 то же самое для rightId

  • для вставки узла освободите место для него, добавив + 2 ко всем узлам с leftId> parent.rightId, затем parent.rightId + = 2, затем вставьте узел с leftId = parent.rightId-2 и rightId = parent. rightId-1

0 голосов
/ 10 июня 2011

Все ваши вопросы могут быть решены очень просто, если вы используете DFS для каждого отношения, а затем снова используете свою функцию calc (), если хотите получить более подробную информацию.

...