Mysql Query从表中选择多级子级

时间:2014-11-13 08:05:15

标签: mysql hierarchical-data

这是我用户表的MySQL表格布局 是否可能

  1. 了解每个人的孩子数量。
  2. 选择在他下面至少有2个孩子的人。
  3. +----+------+--------+
    | id | Name | Parent |
    +----+------+--------+
    | 1  |    A |      0 |
    +----+------+--------+
    | 2  |    B |      0 |
    +----+------+--------+
    | 3  |    C |      1 |
    +----+------+--------+
    | 4  |    D |      3 |
    +----+------+--------+
    | 5  |    E |      2 |
    +----+------+--------+
    

    预期答案
     1.

    +----+------+----------+
    | id | Name | Children |
    +----+------+----------+
    | 1  |    A |  2(C, D) |
    +----+------+----------+
    | 2  |    B |     1(E) |
    +----+------+----------+
    | 3  |    C |     1(D) |
    +----+------+----------+
    | 4  |    D |        0 |
    +----+------+----------+
    | 5  |    E |        0 |
    +----+------+----------+
    

    2

    +----+------+----------+
    | id | Name | Children |
    +----+------+----------+
    | 1  |    A |  2(C, D) |
    +----+------+----------+
    

3 个答案:

答案 0 :(得分:2)

MySQL不支持递归查询,因此如果以存储数据的方式存储数据,这个问题就很难解决(parent表示层次关系)。

您可以通过几种不同的方式存储层次结构,以便更轻松地解决此问题。我在这里做了一个关于此的演讲:Models for Hierarchical Data with SQL and PHP

我最喜欢的解决方案我称之为 Closure Table 。在此设计中,您使用第二个表来存储层次结构中的所有路径。包括长度为零的路径,它将每个节点连接到自身,因为这样可以在以后更轻松地完成某些任务。

CREATE TABLE TreePaths (
  ancestor INT NOT NULL,
  descendant INT NOT NULL,
  length INT NOT NULL DEFAULT 0
  PRIMARY KEY (ancestor, descendant)
);

INSERT INTO TreePaths VALUES
(1,1,0), (1,3,1), (1,4,2),
(2,2,0), (2,5,1),
(3,3,0), (3,4,1),
(4,4,0),
(5,5,0);

然后,您可以查询给定节点的所有子项:

SELECT descendant FROM TreePaths WHERE ancestor = 1 AND length > 0;

您可以通过按祖先分组并使用HAVING选择组来限制至少有两个孩子的节点:

SELECT ancestor, COUNT(*), GROUP_CONCAT(descendant) FROM TreePaths WHERE length > 0
GROUP BY ancestor HAVING COUNT(*) >= 2;

答案 1 :(得分:0)

正如评论所示,递归查询是不可能的。所以我担心,你的第一个问题没有令人满意的答案。

然而,在第二个问题中,你提到你想拥有所有至少有2个孩子的超级用户(在这里我假设你的意思是至少有两个孩子的水平)。

    SELECT id, name, CONCAT(level,children1,children2) AS children 
    FROM 
       (SELECT 
          t1.id, 
          t1.name, 
          IF(t2.name IS NOT NULL, t1.level + 1, t1.level) AS level, 
          IF(t1.children1 IS NULL, '', CONCAT('(',t1.children1)) AS children1, 
          IF(t2.name IS NULL, IF(t1.children1 IS NULL, '', ')'), CONCAT(', ', t2.name, ')')) AS children2 
        FROM 
          (SELECT u1.id, u1.name, u2.id AS bridge, u2.name AS children1, IF(u2.name IS NOT NULL, 1, 0) AS level 
           FROM users u1 
           LEFT JOIN users u2 ON u1.parent = 0 AND u2.parent = u1.id) t1 
        LEFT JOIN users t2 ON t2.parent = t1.bridge) x

这将检索所有超级父母(parent = 0)及其前两个级别的孩子(回答问题1并定义级别)。如果你添加

        WHERE level > 1

查询,您将获得所有至少有2个孩子级别的所有超级父母的筛选列表(回答问题2)。

让所有孩子都成为一个超级父母实际上只是很难,因为连续保存父值而不是孩子的值(如果在你的架构元素中没有兄弟姐妹)。另一种方法是使用增量变量轻松 。在您的情况下,如果您想要找到D的完整遗产,您可以运行

    SELECT t2.id, t2.name, level
    FROM (
        SELECT @r AS _id, (
            SELECT @r := parent 
            FROM users 
            WHERE id = _id
            ) AS parent, @l := @l + 1 AS level 
        FROM (
            SELECT @r := 4, @l := 0
            ) vars, users u 
        WHERE @r <> 0
        ) t1 
    JOIN users t2 ON t1._id = t2.id 
    ORDER BY t1.level DESC

其中@r最初设置为D的id值。查询返回元素本身以及单独行中的每个父级以及反转级别:

id  name level
1   A    3
3   C    2
4   D    1

当然,由于动态变量,这只能为每个元素单独运行,但是它会为您提供从子项到最上层父项的完整行。

答案 2 :(得分:0)

如果我读了你的问题,你需要知道谁是孩子和孙子。 如果这是真的,试试这个:

Select  a.id, a.name, 
        cast(
            (Case When b.name is Null Then 0 Else 1 End)+
            (Case When c.name is Null Then 0 Else 1 End)as Varchar(2)) +
        Case    When b.name is null Then ''
                When c.name is null Then '('+b.name+')'
                Else '('+b.name+','+c.name+')' End as Child
From [user] a
Left Join [user] b
    On b.parent = a.id
Left Join [user] c
    On c.parent = b.id

如果您想选择在他下面至少有2个孩子的人。 你只需添加这个过滤器:

Where (Case When b.name is Null Then 0 Else 1 End)+(Case When c.name is Null Then 0 Else 1 End) >= 2