在嵌套集中移动节点

时间:2009-05-20 18:37:08

标签: mysql tree hierarchical-data nested-sets

我需要一个MySQL查询,将一个节点及其所有子节点移动到嵌套集中。我找到了this网站,但是这个功能看似不合逻辑 - 嵌套集模型中没有universeidtreeid,代码本身只比感觉要求的长。我在表中唯一的额外列是parent

我不能再删除并添加节点,因为它会丢失其ID。

13 个答案:

答案 0 :(得分:46)

我知道,这个话题已经很老了,但无论如何它仍然没有答案。我从谷歌来到这里,并没有找到这个问题的直接答案。

因此,经过一番研究后,我找到了相当简单的解决方案。

一切,我们需要移动我们的节点是:节点左右位置,新父节点右位置。然后,可以通过四个简单的步骤移动到新位置的节点:

  1. 将节点及其所有子节点的位置更改为负值, 它与模块中的当前值相等。
  2. 将所有位置“向上”移动,这更像是当前节点的pos_right。
  3. 将所有位置“向下”移动,这更像是新父节点的pos_right。
  4. 更改当前节点及其所有子节点的位置,以便它现在正好是新父节点的“之后”(或“向下”)。
  5. 这就是理论,现在 - MySQL中的这种算法实现(使用PHP的例子):

    -- step 0: Initialize parameters.
    SELECT
        @node_id := 1, --put there id of moving node 
        @node_pos_left := 0, --put there left position of moving node
        @node_pos_right := 1, --put there right position of moving node
        @parent_id := 2, --put there id of new parent node (there moving node should be moved)
    
        @parent_pos_right := 4; --put there right position of new parent node (there moving node should be moved)
    SELECT
        @node_size := @node_pos_right - @node_pos_left + 1; -- 'size' of moving node (including all it's sub nodes)
    
    -- step 1: temporary "remove" moving node
    
    UPDATE `list_items`
    SET `pos_left` = 0-(`pos_left`), `pos_right` = 0-(`pos_right`)
    WHERE `pos_left` >= @node_pos_left AND `pos_right` <= @node_pos_right;
    
    -- step 2: decrease left and/or right position values of currently 'lower' items (and parents)
    
    UPDATE `list_items`
    SET `pos_left` = `pos_left` - @node_size
    WHERE `pos_left` > @node_pos_right;
    UPDATE `list_items`
    SET `pos_right` = `pos_right` - @node_size
    WHERE `pos_right` > @node_pos_right;
    
    -- step 3: increase left and/or right position values of future 'lower' items (and parents)
    
    UPDATE `list_items`
    SET `pos_left` = `pos_left` + @node_size
    WHERE `pos_left` >= IF(@parent_pos_right > @node_pos_right, @parent_pos_right - @node_size, @parent_pos_right);
    UPDATE `list_items`
    SET `pos_right` = `pos_right` + @node_size
    WHERE `pos_right` >= IF(@parent_pos_right > @node_pos_right, @parent_pos_right - @node_size, @parent_pos_right);
    
    -- step 4: move node (ant it's subnodes) and update it's parent item id
    
    UPDATE `list_items`
    SET
        `pos_left` = 0-(`pos_left`)+IF(@parent_pos_right > @node_pos_right, @parent_pos_right - @node_pos_right - 1, @parent_pos_right - @node_pos_right - 1 + @node_size),
        `pos_right` = 0-(`pos_right`)+IF(@parent_pos_right > @node_pos_right, @parent_pos_right - @node_pos_right - 1, @parent_pos_right - @node_pos_right - 1 + @node_size)
    WHERE `pos_left` <= 0-@node_pos_left AND `pos_right` >= 0-@node_pos_right;
    UPDATE `list_items`
    SET `parent_item_id` = @parent_id
    WHERE `item_id` = @node_id;
    

    请注意 - SQL代码中可能仍存在一些语法错误,因为我实际上在PHP中使用此算法,如下所示:

    $iItemId = 1;
    $iItemPosLeft = 0;
    $iItemPosRight = 1;
    $iParentId = 2;
    $iParentPosRight = 4;
    $iSize = $iPosRight - $iPosLeft + 1;
    $sql = array(
    
        // step 1: temporary "remove" moving node
    
        'UPDATE `list_items`
        SET `pos_left` = 0-(`pos_left`), `pos_right` = 0-(`pos_right`)
        WHERE `pos_left` >= "'.$iItemPosLeft.'" AND `pos_right` <= "'.$iItemPosRight.'"',
    
        // step 2: decrease left and/or right position values of currently 'lower' items (and parents)
    
        'UPDATE `list_items`
        SET `pos_left` = `pos_left` - '.$iSize.'
        WHERE `pos_left` > "'.$iItemPosRight.'"',
        'UPDATE `list_items`
        SET `pos_right` = `pos_right` - '.$iSize.'
        WHERE `pos_right` > "'.$iItemPosRight.'"',
    
        // step 3: increase left and/or right position values of future 'lower' items (and parents)
    
        'UPDATE `list_items`
        SET `pos_left` = `pos_left` + '.$iSize.'
        WHERE `pos_left` >= "'.($iParentPosRight > $iItemPosRight ? $iParentPosRight - $iSize : $iParentPosRight).'"',
        'UPDATE `list_items`
        SET `pos_right` = `pos_right` + '.$iSize.'
        WHERE `pos_right` >= "'.($iParentPosRight > $iItemPosRight ? $iParentPosRight - $iSize : $iParentPosRight).'"',
    
        // step 4: move node (ant it's subnodes) and update it's parent item id
    
        'UPDATE `list_items`
        SET
            `pos_left` = 0-(`pos_left`)+'.($iParentPosRight > $iItemPosRight ? $iParentPosRight - $iItemPosRight - 1 : $iParentPosRight - $iItemPosRight - 1 + $iSize).',
            `pos_right` = 0-(`pos_right`)+'.($iParentPosRight > $iItemPosRight ? $iParentPosRight - $iItemPosRight - 1 : $iParentPosRight - $iItemPosRight - 1 + $iSize).'
        WHERE `pos_left` <= "'.(0-$iItemPosLeft).'" AND i.`pos_right` >= "'.(0-$iItemPosRight).'"',
        'UPDATE `list_items`
        SET `parent_item_id` = "'.$iParentItemId.'"
        WHERE `item_id`="'.$iItemId.'"'
    );
    
    foreach($sql as $sqlQuery){
        mysql_query($sqlQuery);
    }
    

    请注意,该代码可能已经过优化,但为了更好的可读性,我将保留这些代码。如果在多用户系统中使用嵌套集,也请考虑表锁定。

    希望我的信息能帮助任何人,他会在我之后寻找解决方案。任何评论和更正也是受欢迎的。

答案 1 :(得分:13)

这是一个解决方案,允许您将节点移动到树中的任何位置,作为兄弟或只有一个输入参数的子节点 - 节点的新左侧位置(newlpos)。

基本上有三个步骤:

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

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

//
 *  -- create new space for subtree
 *  UPDATE tags SET lpos = lpos + :width WHERE lpos >= :newlpos
 *  UPDATE tags SET rpos = rpos + :width WHERE rpos >= :newlpos
 * 
 *  -- 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 = newlpos - 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

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

答案 2 :(得分:2)

我知道这是一个老问题,但我只是自己使用了答案,但对于SQL Server。如果有人想要它,这里是基于接受的答案的SQL Server存储过程的代码。

CREATE PROCEDURE [dbo].[Item_Move] 
    @id uniqueidentifier, 
    @destinationId uniqueidentifier
AS
BEGIN

    SET NOCOUNT ON;

    declare @moverLeft int,
            @moverRight int,
            @destinationRight int,
            @node_size int

    -- step 0: Initialize parameters.
    SELECT 
        @moverLeft = leftExtent, 
        @moverRight = rightExtent 
    FROM 
        Item 
    WHERE 
        id = @id

    SELECT 
        @destinationRight = rightExtent 
    FROM 
        Item 
    WHERE 
        id = @destinationId

    SELECT
        @node_size = @moverRight - @moverLeft + 1; -- 'size' of moving node (including all it's sub nodes)

    -- step 1: temporary "remove" moving node
    UPDATE Item
    SET leftExtent = 0-(leftExtent), rightExtent = 0-(rightExtent), updatedDate = GETDATE()
    WHERE leftExtent >= @moverLeft AND rightExtent <= @moverRight;

    -- step 2: decrease left and/or right position values of currently 'lower' items (and parents)
    UPDATE Item
    SET leftExtent = leftExtent - @node_size, updatedDate = GETDATE()
    WHERE leftExtent > @moverRight;
    UPDATE Item
    SET rightExtent = rightExtent - @node_size, updatedDate = GETDATE()
    WHERE rightExtent > @moverRight;

    -- step 3: increase left and/or right position values of future 'lower' items (and parents)
    UPDATE Item
    SET leftExtent = leftExtent + @node_size, updatedDate = GETDATE()
    WHERE leftExtent >= CASE WHEN @destinationRight > @moverRight THEN @destinationRight - @node_size ELSE @destinationRight END;
    UPDATE Item
    SET rightExtent = rightExtent + @node_size, updatedDate = GETDATE()
    WHERE rightExtent >= CASE WHEN @destinationRight > @moverRight THEN @destinationRight - @node_size ELSE @destinationRight END;

    -- step 4: move node (and it's subnodes) and update it's parent item id
    UPDATE Item
    SET
        leftExtent = 0-(leftExtent) + CASE WHEN @destinationRight > @moverRight THEN @destinationRight - @moverRight - 1 ELSE @destinationRight - @moverRight - 1 + @node_size END,
        rightExtent = 0-(rightExtent) + CASE WHEN @destinationRight > @moverRight THEN @destinationRight - @moverRight - 1 ELSE @destinationRight - @moverRight - 1 + @node_size END, 
        updatedDate = GETDATE()
    WHERE leftExtent <= 0-@moverLeft AND rightExtent >= 0-@moverRight;
    UPDATE Item
    SET parentId = @destinationId, updatedDate = GETDATE()
    WHERE id = @id;


END

答案 3 :(得分:1)

在嵌套集设计中,移动子树非常昂贵且复杂。

您应该考虑使用不同的设计来表示树木。

例如,如果使用Path Enumeration设计,则将每个节点的直接祖先列表存储为连接字符串。

id path
 1  1/
 2  1/2/
 3  1/3/
 4  1/3/4/
 5  1/3/5/

然后移动子树(比如节点3移动到节点2的子节点):

UPDATE Tree t
 JOIN Tree node2 ON (node2.id = 2)
 JOIN Tree node3 ON (node3.id = 3)
SET t.path = CONCAT(node2.path, REPLACE(t.path, node3.path, node2.path))
WHERE t.path LIKE CONCAT(node3.path, '%');

答案 4 :(得分:0)

感谢将lft和rgt转换为负面对手的想法。 我在这里发布了一个更通用的方法:Move node in Nested Sets tree

queryBatch()函数将查询包含在事务中。

答案 5 :(得分:0)

# Get Left and Right offsets of both source node (Drag Node) and target node (Drop off Node).
SELECT lft,rgt INTO @sourceNodeLft,@sourceNodeRgt FROM treetest WHERE id=_sourceNodeId;
SELECT lft,rgt INTO @targetNodeLft,@targetNodeRgt FROM treetest WHERE id=_targetNodeId;

# Determine node order direction
SET @direction := IF(@targetNodeLft<@sourceNodeLft,'UP','DOWN'); 

# Determine with of source node (Drag Node)
SET @width := @sourceNodeRgt - @sourceNodeLft + 1;

# Mark all displaced nodes with negative lft and rgt
UPDATE treetest SET lft = 0-lft, rgt = 0-rgt 
                                WHERE lft >= @targetNodeLft AND rgt <= @targetNodeRgt;
UPDATE treetest SET lft = 0-lft, rgt = 0-rgt 
                                WHERE lft >= @sourceNodeLft AND rgt <= @sourceNodeRgt;


# Update left and right offsets of inner nodes between source (Drag Node)and target (Drop off) node
UPDATE treetest SET lft =   (lft + IF(@direction = 'UP',@width,0-@width)),
                                    rgt = (rgt + IF(@direction = 'UP',@width,0-@width))
                                WHERE lft > IF(@direction = 'UP', @targetNodeLft, @sourceNodeLft)
                                            AND rgt < IF(@direction = 'UP', @sourceNodeLft, @targetNodeLft);


# Update source (Drag) Node and its children offsets  
SET @sourceOffset := IF(@direction = 'UP',@targetNodeLft - @sourceNodeLft, @targetNodeRgt - @width - @sourceNodeLft+1);
UPDATE treetest SET lft = 0 - lft + @sourceOffset, 
                                        rgt = 0 - rgt + @sourceOffset
                                WHERE (0-lft) >= @sourceNodeLft AND (0-rgt) <= @sourceNodeRgt;

# Update target (Drop off) node and its children offsets
SET @targetOffset := IF(@direction = 'UP', 0 - @width,@width);
UPDATE treetest SET lft = 0 - (lft + @targetOffset),
                                        rgt = 0 - (rgt + @targetOffset)
                                WHERE (0-lft) >= @targetNodeLft AND (0-rgt) <= @targetNodeRgt;


答案 6 :(得分:0)

请参阅我博客中有关在MySQL中存储和使用分层数据的文章:

要在这样的表中移动整个分支,您只需要更新根parent(单行)

您需要创建一个功能:

CREATE FUNCTION hierarchy_connect_by_parent_eq_prior_id(value INT) RETURNS INT
NOT DETERMINISTIC
READS SQL DATA
BEGIN
        DECLARE _id INT;
        DECLARE _parent INT;
        DECLARE _next INT;
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET @id = NULL;

        SET _parent = @id;
        SET _id = -1;

        IF @id IS NULL THEN
                RETURN NULL;
        END IF;

        LOOP
                SELECT  MIN(id)
                INTO    @id
                FROM    t_hierarchy
                WHERE   parent = _parent
                        AND id > _id;
                IF @id IS NOT NULL OR _parent = @start_with THEN
                        SET @level = @level + 1;
                        RETURN @id;
                END IF;
                SET @level := @level - 1;
                SELECT  id, parent
                INTO    _id, _parent
                FROM    t_hierarchy
                WHERE   id = _parent;
        END LOOP;
END

并在查询中使用它:

SELECT  CONCAT(REPEAT('    ', level - 1), CAST(hi.id AS CHAR)) AS treeitem, parent, level
FROM    (
        SELECT  hierarchy_connect_by_parent_eq_prior_id(id) AS id, @level AS level
        FROM    (
                SELECT  @start_with := 0,
                        @id := @start_with,
                        @level := 0
                ) vars, t_hierarchy
        WHERE   @id IS NOT NULL
        ) ho
JOIN    t_hierarchy hi
ON      hi.id = ho.id

答案 7 :(得分:0)

这很简单,首先定义一个存储过程:

   CREATE DEFINER=`root`@`localhost` PROCEDURE `move_item`(
        IN itemId BIGINT, IN kind SMALLINT, 
        IN newSiblingId BIGINT UNSIGNED, IN newSiblingKind SMALLINT, IN newParentId BIGINT UNSIGNED,
        IN jobId BIGINT UNSIGNED, IN companyId BIGINT UNSIGNED,
        OUT outSucess SMALLINT UNSIGNED)
proc_label:BEGIN

接下来我们需要一些局部变量:

DECLARE oldLeft, oldRight, newLeft, newRight, itemWidth, moveBy INT UNSIGNED DEFAULT 0; 
set outSucess =0;

现在让我们的老左派&amp;正确并获得宽度

SELECT `LFT`, `RGT` into oldLeft, oldRight from `nodes` where `ID`=itemId LIMIT 1;
SET itemWidth = oldRight - oldLeft + 1;

现在乘以-1“

将它们”从树中取出“
UPDATE `nodes` SET `RGT`=`RGT`* -1, `LFT`=`LFT`* -1 WHERE ``LFT` BETWEEN oldLeft and oldRight;

下一部分没有必要,因为没有它就可以使用树,但它很整洁;缩小差距:

-- Update right
UPDATE `nodes` SET `RGT` = `RGT` - itemWidth WHERE `RGT` > oldRight;
-- Update left
UPDATE `nodes` SET `LFT` = `LFT` - itemWidth WHERE `LFT` > oldRight;

现在找到新位置:

SELECT (`RGT`+1) into newLeft from `nodes`  where `ID`=newSiblingId LIMIT 1;

-- No sibling, so make it last in parent
IF (newLeft = 0) AND (newParentId != 0) THEN
  SELECT `RGT` into newLeft from `nodes` WHERE `ID`=newParentId LIMIT 1;
END IF;

 -- If no previous sibling or parent, set to first item in tree
IF (newLeft=0) OR (newLeft=NULL) THEN SET newLeft=1;
END IF;

现在腾出一些空间:

 -- Update right
UPDATE `nodes` SET `RGT` = `RGT` + itemWidth WHERE `RGT` >= newLeft;
-- Update left
UPDATE `nodes` SET `LFT` = `LFT` + itemWidth WHERE `LFT` >= newLeft;

最后将从树中移出的节点移回* -1,当你在它时,将它们移动到正确的位置:

SET moveBy = OldLeft - NewLeft;
UPDATE `nodes` SET `RGT`=(`RGT`* -1)-moveBy, `LFT`=(`LFT`* -1)-moveBy WHERE `LFT` < 0;
set outSucess =1;

未经过测试,粘贴,调整和简化工作程序。

答案 8 :(得分:0)

我相信通过两个额外的列来存储原始节点的左右值(以及所有后续子节点),可以简化算法。我用铅笔和纸做过例子,如果你发现算法上有任何漏洞,请告诉我。

目标节点(您正在移动的Node的新父节点)是tNode。目标节点的左值是tNode.L,右值是tNode.R。类似地,您正在移动的节点是mNode,mNode的左右值是mNode.L和mNode.R。两个额外的列是mNode.SL和mNode.SR

总而言之,我们有4列用于操纵R,L,SL和SR


步骤1

计算

delta1 = (mNode.R - mNode.L) + 1 

步骤2

将mNode原始L和R保存到SL和SR列

- For All L between mNode.L and mNode.R 
   mNode.SL = mNode.L ; mNode.L = 0 ;
 - For All R between mNode.L and mNode.R 
   mNode.SR = mNode.R ; mNode.R = 0 ;

步骤3

Do For all Nodes
IF L > mNode.SR 
   L = L + delta1
IF R > mNode.SR
   R = R + delta1

现在mNode与树分离,并且在没有mNode的情况下调整树。

步骤4

计算

delta2 = (tNode.R - mNode.SL)

步骤5

Do for all Nodes
  IF L >= tNode.R
    L = L + delta1
  IF R >= tNode.R
    R = R + delta1

现在我们已经调整了树(和目标节点)以接受已删除的节点数。

步骤6

在tNode处附加mNode并重置SL / SR列值

Do for all Nodes
 IF SL between mNode.SL and mNode.SR
    L = mNode.SL + delta2 ; mNode.SL = 0  ;
 IF SR between mNode.SL and mNode.SR
    R = mNode.SR + delta2 ; mNode.SR = 0 ;

完成所有这些步骤后,我们应该将mNode移到tNode下。

答案 9 :(得分:0)

已经有很多答案,但是我觉得我的方法对某人可能有用。 基于Roger Keays的回答(非常感谢!),我为mySQL数据库编写了存储过程:

-- to move target before specified node
CREATE DEFINER=`root`@`%` PROCEDURE `move_before`(IN target_id int, before_id int)
BEGIN
    SELECT @new_pos := lft FROM dirs WHERE  id = before_id; 
    CALL  move(target_id, @new_pos);
END

-- to move target after specified node
CREATE DEFINER=`root`@`%` PROCEDURE `move_after`(IN target_id int, after_id int)
BEGIN
    SELECT @new_pos := rgt + 1 FROM dirs WHERE  id = after_id;
    CALL  move(target_id, @new_pos);
END

-- to move target to the specified node
CREATE DEFINER=`root`@`%` PROCEDURE `move_in`(IN target_id int, parent_id int)
BEGIN
    SELECT @new_pos := rgt FROM dirs WHERE  id = parent_id;
    CALL  move(target_id, @new_pos);
END

--main procedure to move target before position 
CREATE DEFINER=`root`@`%` PROCEDURE `move`(in target_id int, in  new_pos int)
BEGIN

    SELECT @oldlft :=  lft, @oldrgt :=  rgt 
    FROM dirs 
    WHERE target_id =  id;

    SET @width := @oldrgt - @oldlft +1;
    SET @distance :=  new_pos - @oldlft;
    SET @tmppos := @oldlft;

    IF (@distance <0)
    THEN
        SELECT @distance := @distance - @width;
        SELECT @tmppos := @tmppos + @width;
    END IF;

    -- create new space for subtree
    UPDATE dirs SET lft = lft + @width WHERE lft >=  new_pos;
    UPDATE dirs SET rgt = rgt + @width WHERE rgt >=  new_pos;

    -- move subtree into new space
    UPDATE dirs SET lft = lft + @distance, rgt = rgt + @distance
        WHERE lft >= @tmppos AND rgt < @tmppos + @width;

    -- remove old space vacated by subtree
    UPDATE dirs SET lft = lft - @width WHERE lft > @oldrgt;
    UPDATE dirs SET rgt = rgt - @width WHERE rgt > @oldrgt;

END

答案 10 :(得分:0)

我有一个存储过程将嵌套集中的节点移动到新的父节点。我在名为“somedb”的MySQL / InnoDB数据库中使用名为“category”的表。当然,如果目标是要移动的类别的子类别,则此过程会使事情搞砸,因此请确保您不要尝试在自身内部嵌入节点。我将把它作为练习留给读者,使这个程序安全。

CREATE PROCEDURE `somedb`.`moveCatParent` (IN cat_a VARCHAR(45), IN cat_b VARCHAR(45))
BEGIN
    START TRANSACTION;

    /* cat_b.lft + 1 is the destination. */
    SELECT @destination := (lft + 1)
    FROM category
    WHERE name = cat_b;

    SELECT @cat_a_width := ((rgt - lft) + 1)
    FROM category
    WHERE name = cat_a;

    /* Rip this table a new cat_a sized hole inside cat_b. */  
    UPDATE category SET rgt = rgt + @cat_a_width WHERE rgt >= @destination;
    UPDATE category SET lft = lft + @cat_a_width WHERE lft >= @destination;

    SELECT @cat_a_lft := lft, @cat_a_rgt := rgt
    FROM category
    WHERE name = cat_a;

    SELECT @diff := @destination - @cat_a_lft;

    /* Move cat_a and all inhabitants to new hole */  
    UPDATE category SET rgt = rgt + @diff WHERE rgt BETWEEN @cat_a_lft AND @cat_a_rgt;
    UPDATE category SET lft = lft + @diff WHERE lft BETWEEN @cat_a_lft AND @cat_a_rgt;

    /* Close the gap created when we moved cat_a. */
    UPDATE category SET rgt = rgt - @cat_a_width WHERE rgt >= @cat_a_lft;
    UPDATE category SET lft = lft - @cat_a_width WHERE lft >= @cat_a_lft;

    COMMIT;
END

答案 11 :(得分:0)

我知道这篇文章已经过时了但是我发布了这个解决方案,每个人都可以在这里找到解决方案。我发现这个@ sedna-soft.de。我测试了id并且工作得很好

 -- moves a subtree before the specified position
 -- if the position is the rgt of a node, the subtree will be its last child
 -- if the position is the lft of a node, the subtree will be inserted before
 -- @param l the lft of the subtree to move
 -- @param r the rgt of the subtree to move
 -- @param p the position to move the subtree before




 SET @r: , @l: , @p: 

 update tree
 set
 lft = lft + if (@p > @r,
    if (@r < lft and lft < @p,
        @l - @r - 1,
        if (@l <= lft and lft < @r,
            @p - @r - 1,
            0
        )
    ),
    if (@p <= lft and lft < @l,
        @r - @l + 1,
        if (@l <= lft and lft < @r,
            @p - @l,
            0
        )
    )
),
rgt = rgt + if (@p > @r,
    if (@r < rgt and rgt < @p,
        @l - @r - 1,
        if (@l < rgt and rgt <= @r,
            @p - @r - 1,
            0
        )
    ),
    if (@p <= rgt and rgt < @l,
        @r - @l + 1,
        if (@l < rgt and rgt <= @r,
            @p - @l,
            0
        )
    )
        )
  where @r < @p or @p < @l; 

答案 12 :(得分:-1)

$ row是一个数组,表示我必须移动的行; 它一定是这样的:

Array ( [lft] => 5 [rgt] => 10 [width] => 6 ) 

$ row2是一个代表命运节点的数组;

Array ( [id] => 5 [lft] => 2 [rgt] => 17 [width] => 16 ) 

...

mysql_query("UPDATE entryCategory SET rgt = rgt + %d - %d, lft = lft + %d - %d WHERE rgt <= %d and lft >= %d;",$row2["rgt"],$row["lft"],$row2["rgt"],$row["lft"],$row["rgt"],$row["lft"]);
mysql_query("UPDATE entryCategory SET rgt = rgt + %d WHERE id=%d;",$row["width"],$row2["id"]);
mysql_query("UPDATE entryCategory SET rgt = rgt - %d, lft = lft - %d  WHERE rgt > %d and lft > %d;",$row["width"],$row["width"],$row["rgt"],$row["rgt"]);