(Note Solarflare suggested I got this recursion the wrong way around, see children from parents in my edit at the end.)
This is the fastest I could come up with, assuming each child has only one parent, I hope its along the right lines. Note that I changed the table name to agentsZ as there is a drop command at the beginning which will wipe out the original table if run without the Z. The reason for this is so that the stored procedure will run out of the box provided you change the table name to 'agents' and the data column name is replaced with the actual names of the columns you require (asterisk won't work).
Raw code:
# DROP TABLE IF EXISTS agentsZ;
CREATE TABLE agentsZ (id TINYINT UNSIGNED PRIMARY KEY, upline_id TINYINT UNSIGNED, `data` CHAR(8));
INSERT INTO agentsZ
VALUES (1, 4, 'A'),
(2, 3, 'B'),
(5, 8, 'C'),
(6, 7, 'D'),
(4, 9, 'E'),
(3, 9, 'F'),
(9, 12, 'G'),
(8, 11, 'H'),
(7, 10, 'I'),
(12, 13, 'J'),
(11, 14, 'K'),
(10, 14, 'L');
DELIMITER $
DROP PROCEDURE IF EXISTS getParents$
CREATE PROCEDURE getParents(in_id INT)
BEGIN
SET @VUplineID := in_id;
SELECT id, @VUplineID := upline_id upline_id, `data` FROM agentsZ WHERE id = @VUplineID;
END$
DELIMITER ;
CALL getParents(1);
Tested code:
mysql> DROP TABLE IF EXISTS agentsZ;
Query OK, 0 rows affected, 1 warning (0.01 sec)
mysql>
mysql> CREATE TABLE agentsZ (id TINYINT UNSIGNED PRIMARY KEY, upline_id TINYINT UNSIGNED, `data` CHAR(8));
Query OK, 0 rows affected (0.06 sec)
mysql>
mysql> INSERT INTO agentsZ
-> VALUES (1, 4, 'A'),
-> (2, 3, 'B'),
-> (5, 8, 'C'),
-> (6, 7, 'D'),
-> (4, 9, 'E'),
-> (3, 9, 'F'),
-> (9, 12, 'G'),
-> (8, 11, 'H'),
-> (7, 10, 'I'),
-> (12, 13, 'J'),
-> (11, 14, 'K'),
-> (10, 14, 'L');
Query OK, 12 rows affected (0.02 sec)
Records: 12 Duplicates: 0 Warnings: 0
mysql>
mysql> DELIMITER $
mysql>
mysql> DROP PROCEDURE IF EXISTS getParents$
Query OK, 0 rows affected (0.02 sec)
mysql>
mysql> CREATE PROCEDURE getParents(in_id INT)
-> BEGIN
->
-> SET @VUplineID := in_id;
-> SELECT id, @VUplineID := upline_id upline_id, `data` FROM agentsZ WHERE id = @VUplineID;
->
-> END$
Query OK, 0 rows affected (0.00 sec)
mysql>
mysql> DELIMITER ;
mysql>
mysql> CALL getParents(1);
+----+-----------+------+
| id | upline_id | data |
+----+-----------+------+
| 1 | 4 | A |
| 4 | 9 | E |
| 9 | 12 | G |
| 12 | 13 | J |
+----+-----------+------+
4 rows in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
Slightly less elegant, but to go the other way here is another function:
DELIMITER $
DROP PROCEDURE IF EXISTS getChildren$
CREATE PROCEDURE getChildren(in_id INT)
BEGIN
SET @VBeforeRows := -1;
SET @VAfterRows := 0;
SET @VDownLineIDRegex := CONCAT('^', in_id, '$');
WHILE @VAfterRows != @VBeforeRows DO
SET @VBeforeRows := @VAfterRows;
DROP TEMPORARY TABLE IF EXISTS ZResults;
CREATE TEMPORARY TABLE ZResults
SELECT id, upline_id, IF(@VDownLineIDRegex REGEXP CONCAT('\|\^', id, '\$'), @VLoop := FALSE, @VDownLineIDRegex := CONCAT(@VDownLineIDRegex, '|^', id, '$')) idRegex, `data` FROM agentsZ WHERE upline_id REGEXP @VDownLineIDRegex;
SELECT COUNT(*) INTO @VAfterRows FROM ZResults;
END WHILE;
SELECT id, upline_id, `data` FROM ZResults;
END$
DELIMITER ;
CALL getChildren(14);
Here's a copy of my output when executed:
mysql> DROP TABLE IF EXISTS agentsZ;
Query OK, 0 rows affected (0.02 sec)
mysql>
mysql> CREATE TABLE agentsZ (id TINYINT UNSIGNED PRIMARY KEY, upline_id TINYINT UNSIGNED, `data` CHAR(8));
Query OK, 0 rows affected (0.04 sec)
mysql>
mysql> INSERT INTO agentsZ
-> VALUES (1, 4, 'A'),
-> (2, 3, 'B'),
-> (5, 8, 'C'),
-> (6, 7, 'D'),
-> (4, 9, 'E'),
-> (3, 9, 'F'),
-> (9, 12, 'G'),
-> (8, 11, 'H'),
-> (7, 10, 'I'),
-> (12, 13, 'J'),
-> (11, 14, 'K'),
-> (10, 14, 'L');
Query OK, 12 rows affected (0.02 sec)
Records: 12 Duplicates: 0 Warnings: 0
mysql>
mysql> DELIMITER $
mysql>
mysql> DROP PROCEDURE IF EXISTS getChildren$
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql>
mysql> CREATE PROCEDURE getChildren(in_id INT)
-> BEGIN
->
-> SET @VBeforeRows := -1;
-> SET @VAfterRows := 0;
-> SET @VDownLineIDRegex := CONCAT('^', in_id, '$');
->
-> WHILE @VAfterRows != @VBeforeRows DO
->
-> SET @VBeforeRows := @VAfterRows;
->
-> DROP TEMPORARY TABLE IF EXISTS ZResults;
->
-> CREATE TEMPORARY TABLE ZResults
-> SELECT id, upline_id, IF(@VDownLineIDRegex REGEXP CONCAT('\|\^', id, '\$'), @VLoop := FALSE, @VDownLineIDRegex := CONCAT(@VDownLineIDRegex, '|^', id, '$')) idRegex, `data` FROM agentsZ WHERE upline_id REGEXP @VDownLineIDRegex;
->
-> SELECT COUNT(*) INTO @VAfterRows FROM ZResults;
->
-> END WHILE;
->
-> SELECT id, upline_id, `data` FROM ZResults;
->
-> END$
Query OK, 0 rows affected (0.01 sec)
mysql>
mysql> DELIMITER ;
mysql>
mysql> CALL getChildren(14);
+----+-----------+------+
| id | upline_id | data |
+----+-----------+------+
| 5 | 8 | C |
| 6 | 7 | D |
| 7 | 10 | I |
| 8 | 11 | H |
| 10 | 14 | L |
| 11 | 14 | K |
+----+-----------+------+
6 rows in set (0.13 sec)
Query OK, 0 rows affected (0.13 sec)
Regards,
James