Как найти все идентификаторы детей рекурсивно? - PullRequest
15 голосов
/ 13 июня 2009

Я хотел бы получить все идентификаторы от детей в дереве только с MySQL.

У меня есть такая таблица:

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). Есть ли способ, как этого добиться?

Ответы [ 6 ]

15 голосов
/ 13 июня 2009

Для этого есть два основных метода: списки смежности и вложенные списки. Взгляните на Управление иерархическими данными в MySQL .

У вас есть список смежности. Нет, нет способа рекурсивного захвата всех потомков с помощью одного оператора SQL. Если возможно, просто возьмите их все и отобразите их все в коде.

Вложенные наборы могут делать то, что вы хотите, но я стараюсь этого избегать, потому что стоимость вставки записи высока и подвержена ошибкам.

7 голосов
/ 17 октября 2016

Вот простое решение 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 s всех потомков элемента с 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 в результат. Вот мои предлагаемые решения:

Строка идентификаторов через запятую:

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
))
1 голос
/ 20 сентября 2016

создать таблицу, она должна выглядеть следующим образом

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 
1 голос
/ 13 июня 2009

Возможно, вы могли бы сделать это с помощью хранимой процедуры, если это вариант для вас.

В противном случае вы не можете сделать это с помощью одного SQL-оператора.

В идеале вы должны делать рекурсивные вызовы для обхода дерева из вашей программы

0 голосов
/ 05 марта 2015

Видя, что в принципе ответ «нет» или, по крайней мере, не очень прост с одним оператором 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 ();

Мне нужно это, чтобы узнать, какие рекламные акции на основе категорий продуктов применяются к подкатегориям.

0 голосов
/ 13 июня 2009

Ваш вопрос кажется немного неточным. Почему вы хотите иметь их, и что вы имеете в виду, имея их "на дереве"?

Таблица, которую вы получили, представляет собой (реляционный способ представления) дерево.

Если вы хотите, чтобы они находились «в таблице» со строками, в которых хранятся пары (ID 4, ParentID 0), то вам потребуется версия рекурсивного SQL вашего движка SQL, если это поддерживается этим движком.

Я бы не знал конкретно о MySQL, но, насколько я понимаю, они когда-то планировали реализовать рекурсивный SQL с использованием того же синтаксиса, что и Oracle, т.е. с CONNECT BY.

Если вы посмотрите в оглавлении своего руководства ключевые слова, такие как "рекурсивные запросы" или "CONNECT BY", я думаю, вы сможете найти ответ.

(Извините за невозможность предоставить более готовый к употреблению ответ.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...