【问题标题】:Dealing with nested sets in mysql?处理 mysql 中的嵌套集?
【发布时间】:2011-06-09 22:08:28
【问题描述】:

我决定关注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
Williams 左: 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`;

所以这很容易。

但现在我想知道一些事情,
请注意,在实际数据库中以及左/右所有行都有一个唯一的 ID,以及一个包含其邀请者 ID 的“父”列,或者如果没有被邀请,则为 null

  • 假设我想插入David 作为Judith 的孩子,我该怎么做?
  • 假设我想获得 Mary Hart's 父母和父母父母 (array('Henery', 'Joan', 'Mary Hart')),我该怎么做?
  • 假设我想从Joan 中删除William Hart,我该怎么做?

【问题讨论】:

  • 你试过什么?我不想问,但是嵌套集很复杂,而且您的问题听起来像是在众包您的工作。另外,你甚至还没有开始略过问题的难度。例如:“如果我想颠倒 david 和 judith,让 judith 现在取代 david 作为孩子的位置,反之亦然,我该怎么做 [无需重新索引树两次]?”

标签: php mysql nested-sets


【解决方案1】:

要更新/删除,您需要增加/减少分支的所有元素的left/right 值。
您可以找到here 的查询示例。

效率如何我不知道。

嵌套集在更新/插入/删除时对大树的工作非常缓慢。而且选择速度非常快。
所以这个模型只使用静态数据,大部分时间不会改变存储,这棵树不会包含数千个节点(或者任何更新都需要几分钟才能完成)。物化路径的工作速度要快得多。

【讨论】:

  • 所以,如果我正在使用用户表,因此,数据会经常插入,最终会有数十万行,但不会经常更新/删除,但会几乎在每次页面加载时都可以阅读(树结构是我们网站的中心部分)我应该使用嵌套集吗?
  • @OZ 实际上不仅是分支的元素,而且是所有具有 left_id > current_element.right_id 的元素。
  • @OZ_:我不赞成你,因为这不适用于 MySQL。也许 PostGreSql 是这样,Celco 也在这里。
  • @malko,对,谢谢。 @epitaph,pff,这只是现实。我过去花了很多时间检查它——去做吧。如果表将包含 70000 个节点,则插入新节点可能需要更新许多项目,成千上万。
  • @Hailwood,不,不要使用 users 表。层次结构中的用户?尝试物化路径模型。
【解决方案2】:
  • 要获取节点的父节点,您需要 left_id child.right_id 的节点,如果您只希望直接祖先从前一组中选择具有最高 left_id 的节点。

  • 要删除一个节点,将其删除,然后将所有大于已删除元素右 id 的左/右 id 降低两倍。 if( leftId > deleted.leftId ) leftId-=2 与 rightId 相同

  • 要插入一个节点,为其留出一些空间,将 +2 添加到 leftId > parent.rightId 然后 parent.rightId += 2 的所有节点,然后插入 leftId = parent.rightId-2 和 rightId=parent 的节点。 rightId-1

【讨论】:

    【解决方案3】:

    如果您对每个关系使用 DFS,然后如果您希望更详细地使用您的函数 calculate(),您的所有问题都可以非常容易地解决。

    【讨论】:

    • Hailwood:深度优先搜索。
    猜你喜欢
    • 1970-01-01
    • 2014-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-17
    • 1970-01-01
    • 1970-01-01
    • 2011-09-14
    相关资源
    最近更新 更多