簡體   English   中英

如何遞歸查找孩子的所有ID?

[英]How to find all IDs of children recursively?

我想僅使用 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。 有什么方法可以實現嗎?

有兩種基本方法可以做到這一點:鄰接列表和嵌套列表。 看看在 MySQL管理分層數據

你所擁有的是一個鄰接表。 不,沒有一種方法可以使用單個 SQL 語句遞歸地獲取所有后代。 如果可能,只需抓住它們並將它們全部映射到代碼中。

嵌套集可以做你想做的事,但我傾向於避免它,因為插入記錄的成本很高而且容易出錯。

這是一個簡單的單查詢 MySql 解決方案:

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>替換為父元素的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
))

包括根/父元素

OP 要求元素的子元素,上面已回答。 在某些情況下,在結果中包含根/父元素可能很有用。 以下是我建議的解決方案:

逗號分隔的 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
))

創建表它應該如下所示

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 

如果這是您的選擇,您可能可以使用存儲過程來完成它。

否則,您無法使用單個 sql 語句來完成。

理想情況下,您應該進行遞歸調用以從程序中遍歷樹

看到答案基本上不是或至少不是很容易使用單個 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(); 調用它

我需要它來找出哪些基於產品類別的促銷應用於子類別。

你的問題似乎有點不准確。 你為什么想要擁有它們,“在樹上”擁有它們是什么意思?

您擁有的表是(表示關系的方式)樹。

如果您希望它們“在一個表中”,其中的行包含對(ID 4 ,ParentID 0),那么您需要 SQL 引擎的遞歸 SQL 版本來執行此操作,如果該引擎支持的話。

我不會特別了解 MySQL,但我的理解是他們曾經計划使用與 Oracle 相同的語法(即使用 CONNECT BY)來實現遞歸 SQL。

如果您在手冊的目錄中查找諸如“遞歸查詢”或“CONNECT BY”之類的關鍵字,我想您應該能夠找到答案。

(很抱歉無法提供更易於使用的答案。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM