如何更新嵌套集树结构?

时间:2009-04-11 01:23:16

标签: sql hierarchical-data nested-sets updating

我查看了Managing Hierarchical Data in MySQL,但它实际上只涉及在嵌套集模型中添加和删除节点。

我需要能够移动包含和不包含子节点的节点。

我该怎么做?

4 个答案:

答案 0 :(得分:1)

对于树的这种更改,嵌套集设计不是一个好的选择。在移动节点时重新计算右/左数字真的很复杂。

邻接列表是移动子树最简单的树设计:

UPDATE TreeTable
SET parent = $newparent
WHERE id = 123;

Path Enumeration 设计还可以轻松地重定位节点及其子节点:

UPDATE TreeTable
SET path = REPLACE(path, 'A/B/C/', 'A/D/F/') -- MySQL function
WHERE path LIKE 'A/B/C/%';

答案 1 :(得分:1)

使用子节点移动:

在经典嵌套集中,“左”和“右”值都在0..n * 2值的连续块中,将有一系列行向左移动“x”个位置或移动子树时,'x'位于右侧,其中'x'是要移动的左/右值的数量。例如

A: 1,6
   B: 2,3
   C: 4,5
D: 7,8
E: 9,10

如果你将'A'与后代一起移动到'D'和'E'之间,那么'A'右边但是'E'左边的所有内容需要将其左/右索引减少6(与后代'A'的大小):

UPDATE things
SET nsl=nsl+(
    IF nsl BETWEEN 1 AND 6 THEN 6  -- A-C go forward 6
    ELSE -6                        -- D goes back 6
), nsr=nsr+(                       -- same again
    IF nsl BETWEEN 1 AND 6 THEN 6
    ELSE -6
)
WHERE
    nsl BETWEEN 1 AND 6            -- select A-C
    OR nsl BETWEEN 7 AND 8         -- select D

没有子节点的移动更复杂。包含的节点必须返回一个,删除的节点之后的节点都必须返回两个,然后新插入点之后的节点必须前进两个以腾出空间。

虽然可以以与上面相同的方式执行此操作,但它开始变得非常混乱,您可能会考虑其他方法,例如重写 all 左/正确的值手动或使用不同的模式类型,使这些操作更简单,如完整的祖先 - 后代邻接关系。

答案 2 :(得分:1)

这是一个解决方案,它允许您将节点移动到树中的任何位置,只需一个输入参数 - 节点的新左侧位置(newpos)。没有它的孩子,这个方法不能移动节点,但我有兴趣知道你的用例。

基本上有三组:

  • 为子树创建新空间。
  • 将子树移动到此空间。
  • 删除子树腾出的旧空间。

在psuedo-sql中,它看起来像这样:

//
 *  -- create new space for subtree
 *  UPDATE tags SET lpos = lpos + :width WHERE lpos >= :newpos
 *  UPDATE tags SET rpos = rpos + :width WHERE rpos >= :newpos
 * 
 *  -- move subtree into new space
 *  UPDATE tags SET lpos = lpos + :distance, rpos = rpos + :distance
 *           WHERE lpos >= :tmppos AND rpos < :tmppos + :width
 * 
 *  -- remove old space vacated by subtree
 *  UPDATE tags SET lpos = lpos - :width WHERE lpos > :oldrpos
 *  UPDATE tags SET rpos = rpos - :width WHERE rpos > :oldrpos
 */

:distance变量是新旧位置之间的距离,:width是子树的大小,并且:tmppos用于跟踪在更新期间移动的子树。这些变量定义为:

// calculate position adjustment variables
int width = node.getRpos() - node.getLpos() + 1;
int distance = newpos - node.getLpos();
int tmppos = node.getLpos();

// backwards movement must account for new space
if (distance < 0) {
    distance -= width;
    tmppos += width;
}

有关完整的代码示例,请参阅我的博客

http://www.ninthavenue.com.au/how-to-move-a-node-in-nested-sets-with-sql

如果你喜欢这个解决方案,请进行投票。

答案 3 :(得分:0)

考虑移动节点等同于删除节点及其所有子节点(如果有),并在新位置插入节点及其子节点(如果有)。

现在重新阅读这篇技术文章,在你执行删除操作之后计算出整个表的样子,然后你会发现同样的事情。