递归循环 - 父/子树

时间:2017-06-05 22:07:22

标签: mysql recursive-query b-tree

我尝试递归循环并返回所有child_id的根元素9

结构:

 +-- 9
 |   +-- 8
 |       +-- 17
 |       +-- 33
 |       +-- 18
 |   +-- 22
 |       +-- 11
 |       +-- 4

父子链接表:(表名:elements_children)

+----+-----------+----------+
| id | parent_id | child_id |
+----+-----------+----------+
|  1 |         9 |        8 |
|  2 |         8 |       17 |
|  3 |         8 |       33 |
|  4 |         8 |       18 |
|  5 |         9 |       22 |
|  6 |        22 |       11 |
|  7 |        22 |        4 |
|  8 |         3 |        5 |
+----+-----------+----------+

期望的输出 - [8,17,33,18,22,11,4]

程序1(getChildren):

BEGIN

-- CREATE TEMP TABLE
DROP TABLE IF EXISTS total_children;
CREATE TEMPORARY TABLE total_children(
    id INT(11) NOT NULL auto_increment PRIMARY KEY, 
    child_id VARCHAR(255)
);

-- CALL TO PROCEDURE 2
CALL getNodes(rootNode);

-- SELECT child_id's FROM the temp table, then drop the table
SELECT child_id FROM total_children;
DROP TABLE total_children;

END

程序2(getNodes):

BEGIN
-- VARIABLES
DECLARE done BOOLEAN DEFAULT FALSE;
DECLARE childNode VARCHAR(255);

-- CURSOR1
DECLARE cur1 CURSOR FOR SELECT child_id FROM elements_children WHERE parent_id = parentNode;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

OPEN cur1;

-- START LOOP
myloop:LOOP
    FETCH cur1 INTO childNode;
    -- CHECK IF DONE IS TRUE
    IF(done) THEN 
        LEAVE myloop; 
    END IF;
    -- APPEND TO TOTAL
    INSERT INTO total_children(child_id) SELECT childNode;
    -- RECURSIVE
    CALL getNodes(childNode);
END LOOP myloop;
-- END LOOP

-- END CURSOR1
CLOSE cur1;

END
  

我收到错误:递归限制超过200

我将递归限制设置为200并且我知道该过程不应该递归200次,因此我的代码中必须存在错误并且不能停止递归,我相信与done中的myloop: LOOP变量。

问题:为什么我的程序会产生此递归错误?

1 个答案:

答案 0 :(得分:0)

我认为以下存储过程将产生您要求的结果。我设置了一个表,并用你问题中的数据填充它:

DROP TABLE IF EXISTS `parent_child`;

CREATE TABLE `parent_child` (
    `id`            INT(10) UNSIGNED        NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
    `parent_id`     INT(10) UNSIGNED        NOT NULL,
    `child_id`      INT(10) UNSIGNED        NOT NULL,
    PRIMARY KEY (`id`),
    KEY `idx_parent_child_parent_id` (`parent_id`)
)
    ENGINE=MyISAM 
    AUTO_INCREMENT=1 
    DEFAULT CHARSET=utf8 
    COLLATE=utf8_unicode_ci
    COMMENT '';

INSERT INTO `parent_child`
(`id`,`parent_id`,`child_id`)
VALUES
('1','9','8'),
('2','8','17'),
('3','8','33'),
('4','8','18'),
('5','9','22'),
('6','22','11'),
('7','22','4'),
('8','3','5');

然后我创建了一个逐步让孩子接受的程序,直到没有剩下的为止。

DROP PROCEDURE GetChildren;

DELIMITER //

CREATE PROCEDURE GetChildren(root_id INT)
    BEGIN
        SET @list = root_id;
        SET @new_list = root_id;
        SET @maxDepth = 4;
        SET @depth = 0;

        WHILE (@new_list <> "" AND @depth < @maxDepth) DO
            SELECT @new_list as `new_list_before`,@list as `whole_list_before`;
            SET @depth = @depth + 1;
            SET @querystr = CONCAT("SELECT GROUP_CONCAT(`child_id`) as `children` INTO @c FROM `parent_child` WHERE `parent_id` in (?) AND (NOT (`child_id` IN (?)));");
            PREPARE stmt1 FROM @querystr;
            EXECUTE stmt1 USING @new_list,@list;
            IF @c <> "" THEN
                SET @list = CONCAT(@list,",",@c);
            END IF;
            SET @new_list = @c;
            SELECT @new_list as `new_list`,@list as `whole_list`;
            DEALLOCATE PREPARE stmt1;
        END WHILE;

        SELECT @list;
    END //
DELIMITER ;

最后,以下是如何调用root id为9:

CALL GetChildren(9);

这会产生:

@list:
9,8,22,17,33,18

作为参考,这是执行的选择之一:

SELECT GROUP_CONCAT(`child_id`) as `children` 
FROM `parent_child` 
WHERE `parent_id` in (9,8,22) AND (NOT `child_id` IN (9,8,22)) 
GROUP BY `parent_id`;