Question

Imagine I have a table (Mysql myISAM) with a child->parent relationship (categories and multiple levels of subcategories)

+--------+---------+
| id     |parent_id|
+--------+---------+
| 1      |  null   |
| 2      |    1    |
| 3      |    2    |
| 4      |    7    |
| 5      |    1    |
| 6      |    5    |
+--------+---------+

How would you find all children of some ID, like querying for id 1 would output : 2,5,3,6 ? (order has no importance)

So in other words, how to do a reverted children lookup on this parent_link ?

At the moment, I cycle in php and query for the parent_id, then again and concatenate all the results in a string while there are results, but this is so slow...

Was it helpful?

Solution 2

create table my_table(
id int,
parent_id int
);

insert into my_table values
(1,null),
(2,1),
(3,2),
(4,7),
(5,1),
(6,5);

This stored procedure will get you all the children of any given id

DELIMITER $$
DROP PROCEDURE IF EXISTS get_children$$

CREATE PROCEDURE get_children(IN V_KEY INT)
proc:
BEGIN
  DECLARE vid text;
declare oid text;
  DECLARE count int;
  CREATE TEMPORARY TABLE temp_child_nodes(
      id int
    );

  SET vid = V_KEY;
  INSERT INTO temp_child_nodes(id) SELECT id from my_table where parent_id = vid;
  SELECT GROUP_CONCAT(concat("'",id,"'")) INTO oid from my_table where parent_id = vid;

  SET vid = oid;
  SET count = 0;
  SET @val = '';
  WHILE (vid is NOT NULL) DO 

      SET @sql = CONCAT("INSERT INTO temp_child_nodes(id) SELECT id from my_table where parent_id IN (",vid,")");
      PREPARE stmt1 FROM @sql;
      EXECUTE stmt1;
      DEALLOCATE PREPARE stmt1;

      SET @tsql = CONCAT("SELECT GROUP_CONCAT(id) INTO @val from my_table where parent_id IN (", vid, ")");
      PREPARE stmt2 FROM @tsql;
      EXECUTE stmt2;
      DEALLOCATE PREPARE stmt2;
      SET vid = @val;
      SET count = count + 1;
  END WHILE;
  #SELECT count;
  SELECT * from temp_child_nodes; 
  #SELECT oid;
END
$$

DELIMITER ;

CALL get_children(1);

mysql> CALL get_children(1);
+------+
| id   |
+------+
|    2 |
|    5 |
|    3 |
|    6 |
+------+
4 rows in set (0.22 sec)

Query OK, 0 rows affected (0.22 sec)

OTHER TIPS

Ok, so thanks to Deepak code, I managed to write this, a bit shorter readable, it accepts a table as parameter and returns also the depth of the element.

DELIMITER $$

CREATE PROCEDURE get_children(IN V_KEY INT,IN SOURCETABLE VARCHAR(255))
proc:
BEGIN
  DECLARE vid text;
  DECLARE count int;

  DROP TABLE IF EXISTS `temp_child_nodes`;
  CREATE TEMPORARY TABLE temp_child_nodes(id int, depth int);

  SET vid = V_KEY;
  SET @val = '';
  SET count = 0;

  WHILE (vid is NOT NULL) DO 

      SET @sql = CONCAT("INSERT INTO temp_child_nodes(id,depth) SELECT id,'",count,"' from ",SOURCETABLE," where parent_id IN (",vid,")");
      PREPARE stmt1 FROM @sql;
      EXECUTE stmt1;
      DEALLOCATE PREPARE stmt1;


      SET @tsql = CONCAT("SELECT GROUP_CONCAT(id) INTO @val from ",SOURCETABLE," where parent_id IN (", vid, ")");
      PREPARE stmt2 FROM @tsql;
      EXECUTE stmt2;
      DEALLOCATE PREPARE stmt2;
      SET vid = @val;

      SET count = count + 1;
  END WHILE;

  #output data
  SELECT * from temp_child_nodes; 

END
$$

DELIMITER ;

Here is the sqlfiddle demo for your query http://sqlfiddle.com/#!2/ca90e/6

if there can be 'n' number of child then you need to use a stored procedure

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top