计算后代节点[来自MySQL关闭模式]

时间:2015-01-28 23:27:16

标签: mysql hierarchical-data

我使用以下数据结构来表示数据层次结构。

用户表

+----+------+
| id | name |
+----+------+
|  1 | Bob  |
|  2 | Sam  |
|  3 | Joe  |
|  4 | Kirk |
|  5 | Greg |
+----+------+

关系关闭表

+----------+------------+-------+
| ancestor | descendant | depth |
+----------+------------+-------+
|        1 |          1 |     0 |
|        1 |          2 |     1 |
|        1 |          3 |     2 |
|        1 |          4 |     2 |
|        1 |          5 |     3 |
|        2 |          2 |     0 |
|        2 |          3 |     1 |
|        2 |          4 |     1 |
|        2 |          5 |     2 |
|        3 |          3 |     0 |
|        4 |          4 |     0 |
|        4 |          5 |     1 |
|        5 |          5 |     0 |
+----------+------------+-------+

以上数据代表以下(英文 - ese):

  1. Bob有一个儿子:Sam
  2. Sam有两个儿子:Joe和Kirk。
  3. 乔没有儿子。
  4. Kirk有一个儿子:Greg。
  5. 我从以下SQL获取给定用户的儿子:

    SELECT u.*
    FROM closure AS c
        INNER JOIN `user` AS u ON (u.id = c.descendant)
    WHERE c.ancestor = 1 AND c.depth = 1
    

    这很好用。但是我还想在树下一直返回后代的数量。到目前为止我能想出的最好的是:

    SELECT 
        u.*,
        (
            SELECT COUNT(id) FROM `user` WHERE id IN (
                SELECT descendant FROM closure 
                WHERE ancestor = c.descendant
            )
        ) AS descendant_count
    FROM closure AS c
        INNER JOIN `user` AS u ON (u.id = c.descendant)
    WHERE c.ancestor = 1 AND c.depth = 1
    

    上述查询的预期输出为:

    +----+------+------------------+
    | id | name | descendant_count |
    +----+------+------------------+
    |  2 | Sam  |                3 |
    +----+------+------------------+
    

    问题(最后)

    有没有比我更好的方法来获得总数?所有这些子选择都是粗略的。

    更新

    我在意识到这一点,我可能已经为这个例子简化了太多的事情。我有两个子选择来计算,因为我实际上有3个表:类别;项目; category_closure。在我的示例数据中,显然不需要双嵌套子选择。在我的实际数据中有。希望这是有道理的。

1 个答案:

答案 0 :(得分:3)

您不需要子查询。您可以通过再次连接到闭包表来获取每个子项的后代数,以查找其祖先是相应子项的所有节点。然后使用GROUP BY,这样你就可以得到每个孩子的计数。

SELECT 
    u.*,
    COUNT(*) AS descendant_count
FROM closure AS c
    INNER JOIN `user` AS u ON (u.id = c.descendant)
    INNER JOIN closure AS d ON (c.descendant = d.ancestor)
WHERE c.ancestor = 1 AND c.depth = 1
GROUP BY c.descendant