【问题标题】:MYSQL query to return all descendants of a specific parentMYSQL 查询返回特定父级的所有后代
【发布时间】:2016-09-04 03:53:52
【问题描述】:

我有这个父表。我的目标是找出一个 id 的所有后代。 例如下表:

+----------+-----+
| parentId |  id |
+----------+-----+
|  0       |   1 |
|  0       |   2 |
|  0       |   3 |
|  0       |   4 |
|  1       |   5 |
|  1       |  11 |
|  5       |  12 |
| 12       |  13 |
| 14       |  15 |
| 19       |  20 |
| 20       |  24 |
+----------+-----+

给定父母 0 我想得到:

+----+
| Id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
| 11 |
| 12 |
| 13 |
+----+

我的限制/注释: 1. 在最坏的情况下,我可以有 4 个层次结构。 2.我的数据库是MYSQL(这意味着我不能写递归查询)。 3. id_to_id 表很小.. 100 行顶部。

我正在考虑的解决方案是这样的 sql 查询:

SELECT DISTINCT(T.Id)
FROM(
SELECT t1.Id
FROM id_to_id AS t1
LEFT JOIN id_to_id AS t2 ON t2.parentId = t1.Id
LEFT JOIN id_to_id AS t3 ON t3.parentId = t2.Id
LEFT JOIN id_to_id AS t4 ON t4.parentId = t3.Id
WHERE t1.parentId = 0

UNION ALL

SELECT t2.Id as lev2
FROM id_to_id AS t1
LEFT JOIN id_to_id AS t2 ON t2.parentId = t1.Id
LEFT JOIN id_to_id AS t3 ON t3.parentId = t2.Id
LEFT JOIN id_to_id AS t4 ON t4.parentId = t3.Id
WHERE t1.parentId = 0

UNION ALL

SELECT t3.Id as lev3
FROM id_to_id AS t1
LEFT JOIN id_to_id AS t2 ON t2.parentId = t1.Id
LEFT JOIN id_to_id AS t3 ON t3.parentId = t2.Id
LEFT JOIN id_to_id AS t4 ON t4.parentId = t3.Id
WHERE t1.parentId = 0

UNION ALL

SELECT t4.Id as lev4
FROM id_to_id AS t1
LEFT JOIN id_to_id AS t2 ON t2.parentId = t1.Id
LEFT JOIN id_to_id AS t3 ON t3.parentId = t2.Id
LEFT JOIN id_to_id AS t4 ON t4.parentId = t3.Id
WHERE t1.parentId = 0) as T

WHERE T.Id IS NOT NULL;

但是内部查询将被执行 4 次(我错了吗?):

FROM id_to_id AS t1
LEFT JOIN id_to_id AS t2 ON t2.parentId = t1.Id
LEFT JOIN id_to_id AS t3 ON t3.parentId = t2.Id
LEFT JOIN id_to_id AS t4 ON t4.parentId = t3.Id
WHERE t1.parentId = 0) as T

所以我的问题是:

  • 有什么想法可以让它在不加入 4 次的情况下工作吗?或其他 该查询的智能解决方案?
  • 如何编写可以在没有给定参数的情况下对表上的每个 id 执行相同的查询(我可以这样做吗?) - 类似于:
+----+------------+
| Id |  decedents |
+----+------------+
|  0 | 1,2,3,4,...|
|  1 | 5,11,...   |
+----+------------+

谢谢, 伊多

【问题讨论】:

  • 这篇文章似乎适合你:stackoverflow.com/questions/10646833/…干得好! ;)
  • 您是否坚持只使用 SQL 来完成这项工作?我在项目的无限层次结构中遇到了类似的问题,发现它在我最终用于前端的语言中的程序代码非常干净。如果有兴趣,我会发布该解决方案。

标签: mysql sql


【解决方案1】:

在 MySQL 中使用该表无法更轻松地做到这一点。在 Oracle 中,您可以使用 CONNECT BY ... START WITH 子句执行查询。

但是在 MySQL 中,您要么有一个包含每个父项组合的映射表(在您的示例中,对于 id 11,您将有两个条目:[0, 11] 和 [1, 11]。一个对应于它上面的每个层次结构级别。但这可能会破坏表格的目的。

另一种选择是照常参加,参加 4 次。

第三种可能性是编写一个在表上递归循环并输出结果的过程。这也将非常灵活,因为无论您的层次结构有多深。

create function f_recursive_id_to_id_lookup(in parent_id int)
returns varchar(255)
begin
  -- declare a variable that we use later to break the while loop
  declare l_done tinyint default 0;
  -- the result variable
  declare l_result varchar(255) default '';

  -- table to hold parent ids and insert first id from parameter
  drop table if exists tmp_ids;
  create temporary table tmp_ids(id int, unique key (id));
  insert into tmp_ids(id)
  values (parent_id);

  -- table to hold result
  drop table if exists tmp_result;
  create temporary table tmp_result(id int, unique key (id));

  while l_done = 0 do
    -- insert new ids from previous loop (or none)
    insert ignore into tmp_ids(id)
    select id from tmp_result;

    -- insert new children
    insert ignore into tmp_result(id)
    select id
    from id_to_id i2i
      join tmp_ids tid
        on i2i.parentId = tid.id;

    -- if no rows were inserted, break the loop
    -- rows already present in the table are not counted
    if row_count() = 0 then
      set l_done = 1;
    end if;
  end while;

  -- select the result from the temp table to output to console
  select ifnull(group_concat(id),'') into l_result from tmp_results;
  return l_result;
end

您现在可以在查询中使用此函数:

select parentId, f_recursive_id_to_id_lookup(parentId)
from id_to_id;

这将为您提供预期的结果。不过它可能很慢,因为它会对 100 个 parentId 进行 100 次递归查找。还没有涵盖的是仅列出了顶级 ID。这会列出所有 id。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-17
    • 1970-01-01
    • 2020-03-02
    • 2020-04-25
    • 1970-01-01
    • 2014-07-06
    • 1970-01-01
    • 2018-05-06
    相关资源
    最近更新 更多