Переместите узел во вложенный набор

Awk делает это довольно просто:

echo "1.2.14" | awk -F \. {'print $1,$2, $3'} распечатает 1 2 14.

флаг -F указывает разделитель.

Если вы хотите сохранить одно из значений:

firstVariable=$(echo "1.2.14" | awk -F \. {'print $1'})

23
задан Linger 4 September 2012 в 19:02
поделиться

6 ответов

Я вижу, что эта тема довольно старая, но в любом случае она все еще без ответа. Я пришел сюда из Google и не нашел прямого ответа на этот вопрос.

Итак, после небольшого исследования я нашел довольно простое решение.

Все, что нам нужно для перемещения нашего узла, это: положение узла влево и вправо , правое положение нового родительского узла. Затем узел в новую позицию можно переместить за четыре простых шага:

  1. Измените положение узла и всех его подузлов на отрицательные значения, которые равны текущим по модулю.
  2. Переместить все позиции «вверх», которые больше, чем pos_right текущего узла.
  3. Переместить все позиции «вниз», которые больше, чем pos_right нового родительского узла.
  4. Измените позиции текущего узла и всех его подузлов, чтобы он теперь был точно «после» (или «вниз») нового родительского узла.

Это теория, теперь - реализация этого алгоритма в 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);
}

Обратите внимание также, что этот код может быть оптимизирован, но я оставлю его вот так для лучшей читаемости. Также подумайте о блокировке таблиц, если вы используете вложенные наборы в многопользовательских системах.

Надеюсь, что мое сообщение поможет кому угодно, кто будет искать решение после меня. Также приветствуются любые комментарии и исправления.

46
ответ дан 29 November 2019 в 01:08
поделиться

См. Статью в моем блоге о хранении и использовании иерархических данных в MySQL :

Чтобы переместить целую ветвь в такой таблице , вам просто нужно обновить родительского элемента корня (одна строка)

. Вам нужно будет создать функцию:

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
0
ответ дан 29 November 2019 в 01:08
поделиться

Перемещение поддеревьев очень дорого и сложно при проектировании вложенных наборов.

Вам следует подумать о другом дизайне для представления деревьев.

Например, если вы используете схему «Перечисление путей», вы сохраняете список прямых предков каждого узла в виде конкатенированной строки.

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, '%');
1
ответ дан 29 November 2019 в 01:08
поделиться

У меня есть хранимая процедура, которая перемещает узел во вложенном наборе на новый родительский узел. Я использую таблицу под названием «категория» в базе данных MySQL / InnoDB под названием «somedb». Конечно, если место назначения является подкатегорией категории, которую вы хотите переместить, эта процедура все испортит, поэтому убедитесь, что вы не пытаетесь встроить узел внутрь себя. Я оставлю это в качестве упражнения читателю, чтобы сделать эту процедуру безопасной для этого случая.

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
0
ответ дан 29 November 2019 в 01:08
поделиться

Я считаю, что с двумя дополнительными столбцами для хранения исходных правых и левых значений узла (и всех последующих подузлов) алгоритм можно упростить. Я работал с примерами карандашом и бумагой, поэтому, если вы обнаружите какие-либо дыры в алгоритме, сообщите мне.

Целевой узел (новый родительский узел узла, который вы перемещаете) - это 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 отсоединен от Tree, и Tree настраивается без 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

Присоедините mNode к tNode и сбросьте значения столбцов 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.

0
ответ дан 29 November 2019 в 01:08
поделиться

$ 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"]);
-1
ответ дан 29 November 2019 в 01:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: