如何递归查找子项的所有ID?

时间:2009-06-13 11:10:54

标签: sql mysql database recursion nested

我想从仅使用MySQL的树中获取孩子们的所有ID。

我有一张这样的表:

ID parent_id name
1  0         cat1
2  1         subcat1
3  2         sub-subcat1
4  2         sub-subcat2
5  0         cat2

现在我试图以递归方式获取cat1(2,3,4)的所有子ID。有什么办法可以实现吗?

6 个答案:

答案 0 :(得分:15)

执行此操作有两种基本方法:邻接列表和嵌套列表。看看Managing Hierarchical Data in MySQL

你所拥有的是一份邻接名单。没有办法用一个SQL语句递归抓取所有后代。如果可能,只需抓住它们并将它们全部映射到代码中。

嵌套集可以做你想要的,但我倾向于避免它,因为插入记录的成本很高而且容易出错。

答案 1 :(得分:7)

这是一个简单的单查询MySql-solution:

SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
   SELECT @Ids := (
       SELECT GROUP_CONCAT(`ID` SEPARATOR ',')
       FROM `table_name`
       WHERE FIND_IN_SET(`parent_id`, @Ids)
   ) Level
   FROM `table_name`
   JOIN (SELECT @Ids := <id>) temp1
) temp2

只需将<id>替换为父元素&#39; s ID

这将返回一个字符串,其中包含ID = ID元素的所有后代的<id>个,由,分隔。如果您希望返回多行,每行有一个后代,则可以使用以下内容:

SELECT *
FROM `table_name`
WHERE FIND_IN_SET(`ID`, (
   SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
      SELECT @Ids := (
          SELECT GROUP_CONCAT(`ID` SEPARATOR ',')
          FROM `table_name`
          WHERE FIND_IN_SET(`parent_id`, @Ids)
      ) Level
      FROM `table_name`
      JOIN (SELECT @Ids := <id>) temp1
   ) temp2
))

包含root / parent元素

OP询问了一个元素的孩子,上面已经回答了这个问题。在某些情况下,在结果中包含root / parent元素可能很有用。以下是我建议的解决方案:

以逗号分隔的ID字符串:

SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
   SELECT <id> Level
   UNION
   SELECT @Ids := (
       SELECT GROUP_CONCAT(`ID` SEPARATOR ',')
       FROM `table_name`
       WHERE FIND_IN_SET(`parent_id`, @Ids)
   ) Level
   FROM `table_name`
   JOIN (SELECT @Ids := <id>) temp1
) temp2

多行:

SELECT *
FROM `table_name`
WHERE `ID` = <id> OR FIND_IN_SET(`ID`, (
   SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
      SELECT @Ids := (
          SELECT GROUP_CONCAT(`ID` SEPARATOR ',')
          FROM `table_name`
          WHERE FIND_IN_SET(`parent_id`, @Ids)
      ) Level
      FROM `table_name`
      JOIN (SELECT @Ids := <id>) temp1
   ) temp2
))

答案 2 :(得分:1)

你可以用存储过程来完成它,如果这是你的选择。

否则你不能用一个sql语句来做。

理想情况下,您应该进行递归调用以从程序中移动树

答案 3 :(得分:1)

  

创建表格应该如下所示

DROP TABLE IF EXISTS `parent_child`;
CREATE TABLE `parent_child` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `parent_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;

insert  into `parent_child`(`id`,`name`,`parent_id`)
values (1,'cat1',0),(2,'subcat1',1),
(3,'sub-subcat1',2),(4,'sub-subcat2',2),
(5,'cat2',0);
  

创建获取父子元素的函数

DELIMITER $$

USE `yourdatabase`$$

DROP FUNCTION IF EXISTS `GetAllNode1`$$

CREATE DEFINER=`root`@`localhost` FUNCTION `GetAllNode1`(GivenID INT) RETURNS TEXT CHARSET latin1
    DETERMINISTIC
BEGIN
    DECLARE rv,q,queue,queue_children TEXT;
    DECLARE queue_length,front_id,pos INT;
    SET rv = '';
    SET queue = GivenID;
    SET queue_length = 1;
    WHILE queue_length > 0 DO
        SET front_id = queue;
        IF queue_length = 1 THEN
            SET queue = '';
        ELSE
            SET pos = LOCATE(',',queue) + 1;
            SET q = SUBSTR(queue,pos);
            SET queue = q;
        END IF;
        SET queue_length = queue_length - 1;
        SELECT IFNULL(qc,'') INTO queue_children
        FROM (SELECT GROUP_CONCAT(id) AS qc
        FROM `parent_child` WHERE `parent_id` = front_id) A ;
        IF LENGTH(queue_children) = 0 THEN
            IF LENGTH(queue) = 0 THEN
                SET queue_length = 0;
            END IF;
        ELSE
            IF LENGTH(rv) = 0 THEN
                SET rv = queue_children;
            ELSE
                SET rv = CONCAT(rv,',',queue_children);
            END IF;
            IF LENGTH(queue) = 0 THEN
                SET queue = queue_children;
            ELSE
                SET queue = CONCAT(queue,',',queue_children);
            END IF;
            SET queue_length = LENGTH(queue) - LENGTH(REPLACE(queue,',','')) + 1;
        END IF;
    END WHILE;
    RETURN rv;
END$$

DELIMITER ;
  

为欲望输出编写查询

SELECT GetAllNode1(id) FROM parent_child 
or 
SELECT GetAllNode1(id) FROM parent_child  where id =1 //for specific parent's child element 

答案 4 :(得分:0)

通过单个MYSQL语句看到答案基本上没有或至少不是很容易,我将发布我的php / mysql代码来执行层次结构列表..

function createCategorySubArray()
{
    $categories = getSQL("SELECT pos_category_id FROM pos_categories");
    for($i=0;$i<sizeof($categories);$i++)
    {
        //here we need to find all sub categories
        $pos_category_id = $categories[$i]['pos_category_id'];
        $cat_list[$pos_category_id] = recursiveCategory($pos_category_id,array());

    }
    return $cat_list;

}
function recursiveCategory($pos_category_id, $array)
{
    $return = getSql("SELECT pos_category_id FROM pos_categories WHERE parent = $pos_category_id");
    for($i=0;$i<sizeof($return);$i++)
    {
        $sub_cat = $return[$i]['pos_category_id'];
        $array[] = $sub_cat;
        $array = recursiveCategory($sub_cat, $array);
    }
    return $array;
}

然后你叫它 $ cat_array = createCategorySubArray();

我需要这样才能找出基于产品类别的促销活动正在应用于子类别。

答案 5 :(得分:-1)

你的问题似乎有点不精确。你为什么要拥有它们,拥有它们是什么意思,“在树上”?

你得到的表是IS(表示关系的方式)树。

如果你希望它们“在一个表中”包含保存对的行(ID 4,ParentID 0),那么你需要你的SQL引擎版本的递归SQL来执行此操作,如果该引擎支持它。

我不会特别了解MySQL,但我的理解是他们曾经计划使用与Oracle相同的语法实现递归SQL,即使用CONNECT BY。

如果您在手册的目录中查找“递归查询”或“连接点”等关键字,我想您应该能够找到答案。

(很抱歉无法提供更准备好的答案。)