I have an MPTT tree of over 100,000 records stored in MySQL using lft, rght and parent_id columns. Now the left/right values became co
You are rescue me!!! I use mixed tree model, so when the day is coming, my tree (30000+) was corrupted. I learn from both of your tech, but is not recovery, just fully rebuild with lost all of sorting and reverse tree... I think, need to keep in mind older cat_left.... Just for possible integrity. So, it maybe looks like...
DROP PROCEDURE IF EXISTS tree_recover;
DELIMITER |
CREATE PROCEDURE tree_recover ()
MODIFIES SQL DATA
BEGIN
DECLARE currentId, currentParentId CHAR(36);
DECLARE currentLeft INT;
DECLARE startId INT DEFAULT 1;
# Determines the max size for MEMORY tables.
SET max_heap_table_size = 1024 * 1024 * 512;
START TRANSACTION;
# Temporary MEMORY table to do all the heavy lifting in,
# otherwise performance is simply abysmal.
DROP TABLE IF EXISTS `tmp_cat`;
CREATE TABLE `tmp_cat` (
`cat_id` char(36) NOT NULL DEFAULT '',
`cat_parent` char(36) DEFAULT NULL,
`cat_left` int(11) unsigned DEFAULT NULL,
`cat_right` int(11) unsigned DEFAULT NULL,
`cat_left_old` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`cat_id`),
INDEX USING HASH (`cat_parent`),
INDEX USING HASH (`cat_left`),
INDEX USING HASH (`cat_right`),
INDEX USING HASH (`cat_left_old`)
) ENGINE = MEMORY
SELECT `cat_id`,
`cat_parent`,
`cat_left`,
`cat_right`,
`cat_left` as cat_left_old
FROM `catalog`;
# Leveling the playing field.
UPDATE `tmp_cat`
SET `cat_left` = NULL,
`cat_right` = NULL;
# Establishing starting numbers for all root elements.
WHILE EXISTS (SELECT * FROM `tmp_cat` WHERE `cat_parent` IS NULL AND `cat_left` IS NULL AND `cat_right` IS NULL ORDER BY cat_left_old LIMIT 1) DO
UPDATE `tmp_cat`
SET `cat_left` = startId,
`cat_right` = startId + 1
WHERE `cat_parent` IS NULL
AND `cat_left` IS NULL
AND `cat_right` IS NULL
LIMIT 1;
SET startId = startId + 2;
END WHILE;
# Switching the indexes for the cat_left/rght columns to B-Trees to speed up the next section, which uses range queries.
DROP INDEX `cat_left` ON `tmp_cat`;
DROP INDEX `cat_right` ON `tmp_cat`;
DROP INDEX `cat_left_old` ON `tmp_cat`;
CREATE INDEX `cat_left` USING BTREE ON `tmp_cat` (`cat_left`);
CREATE INDEX `cat_right` USING BTREE ON `tmp_cat` (`cat_right`);
CREATE INDEX `cat_left_old` USING BTREE ON `tmp_cat` (`cat_left_old`);
# Numbering all child elements
WHILE EXISTS (SELECT * FROM `tmp_cat` WHERE `cat_left` IS NULL ORDER BY cat_left_old LIMIT 1) DO
# Picking an unprocessed element which has a processed parent.
SELECT `tmp_cat`.`cat_id`
INTO currentId
FROM `tmp_cat`
INNER JOIN `tmp_cat` AS `parents`
ON `tmp_cat`.`cat_parent` = `parents`.`cat_id`
WHERE `tmp_cat`.`cat_left` IS NULL
AND `parents`.`cat_left` IS NOT NULL
ORDER BY `tmp_cat`.cat_left_old DESC
LIMIT 1;
# Finding the element's parent.
SELECT `cat_parent`
INTO currentParentId
FROM `tmp_cat`
WHERE `cat_id` = currentId;
# Finding the parent's cat_left value.
SELECT `cat_left`
INTO currentLeft
FROM `tmp_cat`
WHERE `cat_id` = currentParentId;
# Shifting all elements to the right of the current element 2 to the right.
UPDATE `tmp_cat`
SET `cat_right` = `cat_right` + 2
WHERE `cat_right` > currentLeft;
UPDATE `tmp_cat`
SET `cat_left` = `cat_left` + 2
WHERE `cat_left` > currentLeft;
# Setting cat_left and rght values for current element.
UPDATE `tmp_cat`
SET `cat_left` = currentLeft + 1,
`cat_right` = currentLeft + 2
WHERE `cat_id` = currentId;
END WHILE;
# Writing calculated values back to physical table.
UPDATE `catalog`, `tmp_cat`
SET `catalog`.`cat_left` = `tmp_cat`.`cat_left`,
`catalog`.`cat_right` = `tmp_cat`.`cat_right`
WHERE `catalog`.`cat_id` = `tmp_cat`.`cat_id`;
COMMIT;
DROP TABLE IF EXISTS `tmp_cat`;
END|