MySQL Recursive从父级获取所有子级

Bak*_*aya 7 mysql hierarchy recursive-query hierarchical-data

我有这种情况使用Mysql上的递归查询在一个表上找到lv 2和lv3子...
我正在使用的数据库结构:

id name parent
1    A    0
2    B    0
3    C    0
4    D    1
5    E    1
6    F    2
7    G    2
8    H    3
9    I    3
10   J    4
11   K    4
Run Code Online (Sandbox Code Playgroud)

我期望的结果,当过滤数据时,id = 1,它将产生我期待的结果.

id name parent
4   D     1
5   E     1
10  J     4
11  K     4
Run Code Online (Sandbox Code Playgroud)

或者这就是例证. 插图

我一直在寻找各地,并阅读这个http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/,但我没有找到我正在寻找的结果..
任何帮助将不胜感激, 谢谢

Man*_*ana 21

如果你想获得特定父母的所有等级孩子,那么你应该试试这个

select  id,
        name,
        parent
from    (select * from tablename
         order by parent, id) tablename,
        (select @pv := '1') initialisation
where   find_in_set(parent, @pv) > 0
and     @pv := concat(@pv, ',', id)
Run Code Online (Sandbox Code Playgroud)

  • 请说明您的代码段。仅仅一段代码对用户没有多大帮助。 (3认同)
  • 完美工作,但无法理解它,但非常感谢。 (2认同)
  • 如果孩子的 ID 低于其父母,则似乎并非在所有级别上都有效。有什么解决办法吗?? (2认同)

Tho*_*ker 6

在我看来,在分层表结构中查找所有子节点的关键是首先找到父节点的路径,然后使用FIND_IN_SET来查看所请求的节点是否在该路径中。向上搜索比向下搜索更容易、更高效,因为指向父级的链接已经存在于表中。

\n

那么让我们从这样的层次结构开始:

\n
1 Pets\n\xe2\x94\x9c\xe2\x94\x80 2 Dogs\n\xe2\x94\x82  \xe2\x94\x9c\xe2\x94\x80 3 Katie\n\xe2\x94\x9c\xe2\x94\x80 4 Cats\n\xe2\x94\x82  \xe2\x94\x9c\xe2\x94\x80 5 George\n\xe2\x94\x82  \xe2\x94\x9c\xe2\x94\x80 6 Pete\n\xe2\x94\x82  \xe2\x94\x9c\xe2\x94\x80 7 Alice\n\xe2\x94\x9c\xe2\x94\x80 8 Other\n\xe2\x94\x82  \xe2\x94\x9c\xe2\x94\x80 9 Rabbits\n\xe2\x94\x82  \xe2\x94\x82  \xe2\x94\x9c\xe2\x94\x80 10 Noah\n\xe2\x94\x82  \xe2\x94\x82  \xe2\x94\x9c\xe2\x94\x80 11 Teddy\n\xe2\x94\x82  \xe2\x94\x82  \xe2\x94\x9c\xe2\x94\x80 12 Bella\n\xe2\x94\x82  \xe2\x94\x9c\xe2\x94\x80 13 Rats\n\xe2\x94\x82  \xe2\x94\x82  \xe2\x94\x9c\xe2\x94\x80 14 Henry\n
Run Code Online (Sandbox Code Playgroud)\n

现在您想要查找该类别下的所有子项Other(包含该类别),那么预期结果将是:

\n
8,9,10,11,12,13,14\n
Run Code Online (Sandbox Code Playgroud)\n

现在让我们看一下 Henry 的层次路径。Henry (14)的父级是Rats (13),它的父级是Other (8),最后是Pets (1)。如果我们使用 ID 为 Henry 创建一条路径,它将如下所示:

\n
1,8,13,14\n
Run Code Online (Sandbox Code Playgroud)\n

这就是 MySQL 函数FIND_IN_SET发挥作用的地方。FIND_IN_SET您可以过滤结果,其中可以在逗号分隔列表中找到变量。在此示例中,我们正在查找类别“其他”(8) 中的所有子项,我们可以简单地使用FIND_IN_SET(8, path)

\n

要获取分层表的路径,我想参考我在此处的帖子中的答案MySql: ORDER BYparent and child。我们只需将破折号更改为逗号,这样我们就可以使用该FIND_IN_SET函数。

\n

上面的示例在分层表中看起来像这样:

\n
+----+--------+---------+\n| id | parent | name    |\n+----+--------+---------+\n|  1 |   NULL | Pets    |\n|  2 |      1 | Dogs    |\n|  3 |      2 | Katie   |\n|  4 |      1 | Cats    |\n|  5 |      4 | George  |\n|  6 |      4 | Pete    |\n|  7 |      4 | Alice   |\n|  8 |      1 | Other   |\n|  9 |      8 | Rabbits |\n| 10 |      9 | Noah    |\n| 11 |      9 | Teddy   |\n| 12 |      9 | Bella   |\n| 13 |      8 | Rats    |\n| 14 |     13 | Henry   |\n+----+--------+---------+\n
Run Code Online (Sandbox Code Playgroud)\n

在我的方法中,我将使用一个过程,该过程将递归地调用自身,并继续在路径前面添加所请求的父级,id直到它到达NULL父级。

\n
DELIMITER $$\nCREATE DEFINER=`root`@`localhost` PROCEDURE `PATH`(IN `input` INT, OUT `output` VARCHAR(128))\nBEGIN\n\n  DECLARE _id INT;\n  DECLARE _parent INT;\n  DECLARE _path VARCHAR(128);\n\n  SET `max_sp_recursion_depth` = 50;\n\n  SELECT `id`, `parent`\n  INTO _id, _parent\n  FROM `database`.`table`\n  WHERE `table`.`id` = `input`;\n\n  IF _parent IS NULL THEN\n    SET _path = _id;\n  ELSE\n    CALL `PATH`(_parent, _path);\n    SELECT CONCAT(_path, \',\', _id) INTO _path;\n  END IF;\n\n  SELECT _path INTO `output`;\n\nEND $$\nDELIMITER ;\n
Run Code Online (Sandbox Code Playgroud)\n

我们在查询中需要这些结果SELECT,因此我们FUNCTION也需要一个包装PROCEDURE.

\n
DELIMITER $$\nCREATE DEFINER=`root`@`localhost` FUNCTION `GETPATH`(`input` INT) RETURNS VARCHAR(128)\nBEGIN\n\n  CALL `PATH`(`input`, @path);\n  RETURN @path;\n\nEND $$\nDELIMITER ;\n
Run Code Online (Sandbox Code Playgroud)\n

现在我们可以在查询中使用该路径。在我的工作站上,对于 10000 行的表,只需要一秒多一点的时间。

\n
SELECT `id`, `parent`, `name`, GETPATH(`id`) `path` FROM `database`.`table`;\n
Run Code Online (Sandbox Code Playgroud)\n

输出示例:

\n
+----+--------+---------+-----------+\n| id | parent | name    | path      |\n+----+--------+---------+-----------+\n|  1 |   NULL | Pets    | 1         |\n|  2 |      1 | Dogs    | 1,2       |\n|  3 |      2 | Katie   | 1,2,3     |\n|  4 |      1 | Cats    | 1,4       |\n|  5 |      4 | George  | 1,4,5     |\n|  6 |      4 | Pete    | 1,4,6     |\n|  7 |      4 | Alice   | 1,4,7     |\n|  8 |      1 | Other   | 1,8       |\n|  9 |      8 | Rabbits | 1,8,9     |\n| 10 |      9 | Noah    | 1,8,9,10  |\n| 11 |      9 | Teddy   | 1,8,9,11  |\n| 12 |      9 | Bella   | 1,8,9,12  |\n| 13 |      8 | Rats    | 1,8,13    |\n| 14 |     13 | Henry   | 1,8,13,14 |\n+----+--------+---------+-----------+\n
Run Code Online (Sandbox Code Playgroud)\n

要查找 Other (8) 的所有子级(还包括 Other 本身),我们可以使用相同的查询和过滤器FIND_IN_SET

\n
SELECT `id`, `parent`, `name`, GETPATH(`id`) `path` FROM `database`.`table` WHERE FIND_IN_SET(8, GETPATH(`id`));\n
Run Code Online (Sandbox Code Playgroud)\n

最后是结果。我们在程序中将递归限制为 50 级,但除此之外我们对深度没有限制。

\n
+----+--------+---------+-----------+\n| id | parent | name    | path      |\n+----+--------+---------+-----------+\n|  8 |      1 | Other   | 1,8       |\n|  9 |      8 | Rabbits | 1,8,9     |\n| 10 |      9 | Noah    | 1,8,9,10  |\n| 11 |      9 | Teddy   | 1,8,9,11  |\n| 12 |      9 | Bella   | 1,8,9,12  |\n| 13 |      8 | Rats    | 1,8,13    |\n| 14 |     13 | Henry   | 1,8,13,14 |\n+----+--------+---------+-----------+\n7 rows in set (0,01 sec)\n
Run Code Online (Sandbox Code Playgroud)\n

如果您想要单个值而不是行,那么您可能需要使用GROUP_CONCAT像这样使用:

\n
SELECT GROUP_CONCAT(`id`) `children` FROM `database`.`table` WHERE FIND_IN_SET(8, GETPATH(`id`));\n
Run Code Online (Sandbox Code Playgroud)\n

给你以下结果:

\n
+--------------------+\n| children           |\n+--------------------+\n| 8,9,10,11,12,13,14 |\n+--------------------+\n1 row in set (0,00 sec)\n
Run Code Online (Sandbox Code Playgroud)\n


oll*_*law -10

SELECT *
FROM TABLENAME
WHERE PARENT = 1
UNION
SELECT * 
FROM TABLENAME
WHERE PARENT IN 
    (SELECT ID FROM TABLENAME WHERE PARENT = 1)
Run Code Online (Sandbox Code Playgroud)

  • 该查询不是递归的! (25认同)