Przenieś węzeł w zagnieżdżonym zestawie
Potrzebowałbym zapytania MySQL, które przenosi węzeł i wszystkie jego dzieci w zagnieżdżonym zestawie. Znalazłem tę stronę , ale ta funkcja wydaje się nielogiczna - nie ma universeid
ani treeid
w zagnieżdżonym modelu zestawu, a sam kod jest po prostu dłuższy niż jest to wymagane. Jedyna dodatkowa kolumna jaką mam w tabeli to parent
.
Nie mogłem po prostu usunąć i dodać węzła ponownie, ponieważ straci swój IDENTYFIKATOR.
13 answers
Widzę, że ten temat jest dość stary, ale i tak pozostaje bez odpowiedzi. Dostałem się tu z Google i nie znalazłem bezpośredniej odpowiedzi na to pytanie.
Więc, po kilku badaniach znalazłem dość łatwe rozwiązanie.Wszystko, czego będziemy potrzebować, aby przesunąć nasz węzeł to: węzeł w lewo i w prawo, nowy węzeł w prawo. Węzeł do nowej pozycji może być przesunięty w czterech prostych krokach:
- zmień pozycję węzła i wszystkich jego węzłów podrzędnych na ujemne wartości, które są równe bieżącym modułom.
- przesuń wszystkie pozycje "w górę", które są większe niż pos_right bieżącego węzła.
- przesuń wszystkie pozycje "w dół", które są większe niż pos_right nowego węzła nadrzędnego.
- zmień pozycję bieżącego węzła i wszystkich jego podnodów, tak aby teraz był dokładnie "po" (lub "w dół") nowego węzła nadrzędnego.
To jest teoria , teraz-ten algorytm w MySQL (przykład przy użyciu 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;
Proszę strzeż się - nadal mogą występować błędy składniowe w kodzie SQL, ponieważ faktycznie używam tego algorytmu w PHP w następujący sposób:
$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);
}
Zwróć również uwagę, że kod może być zoptymalizowany, ale zostawię go w ten sposób dla lepszej czytelności. Rozważ również blokowanie tabeli, jeśli używasz zestawów zagnieżdżonych w systemach wielu użytkowników.
Mam nadzieję, że moja wiadomość pomoże każdemu, kto będzie szukał rozwiązania po mnie. Wszelkie uwagi i poprawki są również mile widziane.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-31 21:57:43
Oto rozwiązanie, które pozwala przenieść węzeł do dowolnej pozycji w drzewie, jako rodzeństwo lub dziecko z tylko jednym parametrem wejściowym-Nowa lewa pozycja (newlpos) węzła.
Zasadniczo istnieją trzy etapy:
- Utwórz nową przestrzeń dla podzbioru.
- Przesuń poddrzewo w tę przestrzeń.
- Usuń starą przestrzeń opuszczoną przez poddrzewo.
W psuedo-sql wygląda to tak:
//
* -- 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
*/
Zmienna :distance jest odległość między nowymi i starymi pozycjami,: width jest wielkością poddrzewa, a :tmppos służy do śledzenia przesuwanego poddrzewa podczas aktualizacji. Zmienne te są zdefiniowane jako:
// 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;
}
Pełny przykład kodu znajduje się na moim blogu pod adresem
Http://www.ninthavenue.com.au/how-to-move-a-node-in-nested-sets-with-sql
Jeśli podoba Ci się to rozwiązanie, proszę zagłosuj.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-16 00:17:30
Wiem, że to stare pytanie, ale sam użyłem odpowiedzi, ale dla SQL Server. Jeśli ktoś go chce, oto kod dla serwera SQL przechowywanego Proc na podstawie zaakceptowanej odpowiedzi.
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-14 14:18:50
Przenoszenie podzbiorów jest bardzo kosztowne i skomplikowane w projektowaniu zestawów zagnieżdżonych.
Powinieneś rozważyć inny wzór przedstawiania drzew.
Na przykład, jeśli używasz projektu wyliczenia ścieżki, przechowujesz listę bezpośrednich przodków każdego węzła jako skonkatenowany łańcuch znaków.
id path
1 1/
2 1/2/
3 1/3/
4 1/3/4/
5 1/3/5/
Następnie przesunięcie podzbioru (powiedzmy, że węzeł 3 przenosi się na dziecko węzła 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, '%');
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-20 18:54:20
Zobacz artykuł na moim blogu do przechowywania i korzystania z danych hierarchicznych w MySQL
:
Aby przenieść całą gałąź w takiej tabeli, wystarczy zaktualizować root parent
(pojedynczy wiersz)
Musisz utworzyć funkcję:
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
I użyj go w zapytaniu:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-20 19:04:24
Mam procedurę składowaną, która przenosi węzeł w zagnieżdżonym zestawie do nowego węzła nadrzędnego. Używam tabeli o nazwie "category "w bazie danych MySQL / InnoDB o nazwie"somedb". Oczywiście, jeśli miejsce docelowe jest podkategorią kategorii, którą chcesz przenieść, procedura ta wszystko spieprzy, więc upewnij się, że nie próbujesz osadzić węzła wewnątrz siebie. Pozostawię to jako ćwiczenie czytelnikowi, aby ta procedura była bezpieczna dla tej sprawy.
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-22 20:58:24
Uważam, że z dwoma dodatkowymi kolumnami do przechowywania oryginalnych wartości węzła prawej i lewej (i wszystkich kolejnych podnodów) algorytm można uprościć. Pracowałem na przykładach ołówkiem i papierem, więc jeśli znajdziesz jakieś dziury w algorytmie, daj mi znać.
Węzłem docelowym (nowym rodzicem węzła, który przenosisz) jest tNode. Lewa wartość węzła docelowego to tNode.L, a właściwą wartością jest tNode.R. podobnie węzeł, który przenosisz, to mNode oraz lewe i prawe wartości dla mNode są mNode.L i mNode.R. dwie dodatkowe kolumny to mNode.SL oraz mNode.SR
Więc w sumie mamy 4 kolumny do manipulacji R, L, SL i SR
Krok 1
Oblicz
delta1 = (mNode.R - mNode.L) + 1
Krok 2
Zapisz oryginalny mNode L i R w kolumnach SL i 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 ;
Step3
Do For all Nodes
IF L > mNode.SR
L = L + delta1
IF R > mNode.SR
R = R + delta1
Teraz mNode jest odłączony od drzewa i drzewo jest regulowane bez mNode.
Step4
Oblicz
delta2 = (tNode.R - mNode.SL)
Krok 5
Do for all Nodes
IF L >= tNode.R
L = L + delta1
IF R >= tNode.R
R = R + delta1
Teraz my dostosowano drzewo (i węzeł docelowy), aby zaakceptować liczbę węzłów, które zostały usunięte.
Krok 6
Załącz mNode na tNode i zresetuj wartości kolumn 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 ;
Po tych wszystkich krokach powinniśmy przenieść mNode pod tNode.
Dzięki za pomysł przekształcenia lft i rgt w ich negatywne odpowiedniki. Bardziej ogólne podejście opublikowałem tutaj: Przenieś węzeł w drzewie zagnieżdżonych zestawów .
Funkcja queryBatch() zamyka zapytanie w transakcji.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:05
Jest to dość proste, najpierw zdefiniuj procedurę składowaną:
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
Następnie potrzebujemy zmiennych lokalnych:
DECLARE oldLeft, oldRight, newLeft, newRight, itemWidth, moveBy INT UNSIGNED DEFAULT 0;
set outSucess =0;
Teraz pobierz naszą starą lewą i prawą stronę i uzyskaj szerokość
SELECT `LFT`, `RGT` into oldLeft, oldRight from `nodes` where `ID`=itemId LIMIT 1;
SET itemWidth = oldRight - oldLeft + 1;
Teraz weź je" z drzewa " mnożąc przez -1
UPDATE `nodes` SET `RGT`=`RGT`* -1, `LFT`=`LFT`* -1 WHERE ``LFT` BETWEEN oldLeft and oldRight;
Następna część nie jest konieczna, ponieważ drzewo będzie działać bez niego, ale jest schludne; Zamknij starą lukę:
-- Update right
UPDATE `nodes` SET `RGT` = `RGT` - itemWidth WHERE `RGT` > oldRight;
-- Update left
UPDATE `nodes` SET `LFT` = `LFT` - itemWidth WHERE `LFT` > oldRight;
Teraz znajdź nową lokalizację:
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;
Teraz zrób trochę miejsca:
-- Update right
UPDATE `nodes` SET `RGT` = `RGT` + itemWidth WHERE `RGT` >= newLeft;
-- Update left
UPDATE `nodes` SET `LFT` = `LFT` + itemWidth WHERE `LFT` >= newLeft;
Wreszcie przenieść węzły, które gdzie przesunięte z drzewa z powrotem przez * -1, a gdy już na nim jesteś, przenieś je również w odpowiednie miejsce:
SET moveBy = OldLeft - NewLeft;
UPDATE `nodes` SET `RGT`=(`RGT`* -1)-moveBy, `LFT`=(`LFT`* -1)-moveBy WHERE `LFT` < 0;
set outSucess =1;
Nie testowane, wklejone i dostosowane i uproszczone z procedury roboczej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-22 14:37:20
Wiem, że ten post jest stary, ale zamieszczam To rozwiązanie dla każdego innego, który dotrze tutaj, aby zobaczyć rozwiązanie.Znalazłem to @ sedna-soft.de . Testowałem id i działa idealnie
-- 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;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-25 13:36:00
# 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;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-03-29 07:35:37
Jest już wiele odpowiedzi, ale czuję, że moja może być dla kogoś przydatna. Na podstawie odpowiedzi Rogera Keaysa (dziękuję bardzo!), Napisałem procedury składowane dla bazy danych 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-06-02 08:52:24
$row jest tablicą, która reprezentuje wiersz, który muszę przenieść; to musi być tak:
Array ( [lft] => 5 [rgt] => 10 [width] => 6 )
$ row2 jest tablicą reprezentującą węzeł destiny;
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"]);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-08 19:12:43