如何在同一查询中选择所有直接子项和祖先

时间:2010-02-23 14:38:40

标签: sql mysql tree hierarchical-data nested-sets

我正在使用MySQL中的树结构,使用嵌套集模型进行重构。

我希望你们中的一些SQL专家可以帮我构建一个SELECT查询。

我希望能够使用LIKE匹配一组节点。对于匹配的每个节点,我还需要该节点的祖先的逗号分隔列表,以及该节点的直接子节点的逗号分隔列表。

我不确定从哪里开始 - 如果在单个查询中甚至可以实现这样的事情。 (目前我在循环中使用查询来完成此操作。)我希望的是一个看起来像这样的结果集....

从字符串“qu”开始并查询表“Body”我得到......

Node      | Parent Nodes               | Immediate Children
Quads       Leg, Lower Body, Muslces     Vastus Lateralus, Vastus Medialis, Rectus Femoris
Obliques    Core, Trunk, Muscles         Inner obliques, outer obliques

关于如何在不循环查询的情况下完成此任务的任何建议都将非常感激。

4 个答案:

答案 0 :(得分:0)

在一个查询中?我不打扰。 SQL将是可怕的,甚至可能不那么有效。将每个位拆分为逻辑较小的查询:首先找到所有匹配的节点,然后为每个节点查找所需的额外信息。

答案 1 :(得分:0)

我不是100%确定你想要什么,但如果我理解正确,你可以使用规范化的数据库模式和子查询来实现这一点。

例如:

表“节点” 表“node_parents”

“nodes”表将存储所有节点,“node_parents”将映射不同节点之间的关系。

因此,当您选择LIKE某个节点时,您可以从node_parents获取其所有父节点和子节点。

您可以使用连接或子查询获取额外信息。

答案 2 :(得分:0)

虽然我同意nickf认为这很糟糕且很脏,但它仍然很有趣,所以这里有:

SELECT     base.left_id, base.ancestors, 
           GROUP_CONCAT(children.left_id) children
FROM       (
            SELECT     base.left_id
            ,          GROUP_CONCAT(ancestors.left_id) ancestors
            FROM       nested_set   base
            LEFT JOIN  nested_set   ancestors
            ON         base.left_id     BETWEEN ancestors.left_id 
                                            AND ancestors.right_id
            WHERE      base.name  LIKE '%criteria%'
            GROUP BY   base.left_id
           ) base                                    
LEFT JOIN  nested_set   children
ON         children.left_id BETWEEN base.left_id 
                                AND base.right_id
LEFT JOIN  nested_set   inbetween
ON         inbetween.left_id BETWEEN base.left_id 
                                AND base.right_id
AND        children.left_id  BETWEEN inbetween.left_id 
                                AND inbetween.right_id     
WHERE      inbetween.left_id IS NULL
GROUP BY   base.left_id

基本上,诀窍是分两步解决:首先,解决祖先问题,然后将祖先压缩到一个列表中,然后使用这个结果为孩子们解决它。

祖先部分相对容易,它是我解决方案中from子句中的子查询。 孩子们有点难。它通过捕获所有后代,然后要求基本节点和后代之间不存在任何节点,这基本上限制了后代只有孩子。

此策略还有其他变体可以解决此问题 - 例如,您可以先执行子项,然后使用SELECT列表中的子查询来解析祖先。

答案 3 :(得分:0)

这个问题比我在其他帖子中预料的要困难得多,但我不同意第一个海报回答。

我非常有信心可以使用单个查询。

您需要使用SUBQUERIES并选择。你有没有看过mySQL网站关于Adjacency List Model的非常好的演示。

因为你可以喜欢“节点”,然后可以使用子查询来获取SQL查询以获取所有父节点和父节点。 我做过这样的事情的一个问题绝对是巨大的!但它奏效了。

看看: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

这只是一个小代码片段,显示了如何找到直接的孩子。

SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
FROM nested_category AS node,
    nested_category AS parent,
    nested_category AS sub_parent,
    (
        SELECT node.name, (COUNT(parent.name) - 1) AS depth
        FROM nested_category AS node,
        nested_category AS parent
        WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.name = 'PORTABLE ELECTRONICS'
        GROUP BY node.name
        ORDER BY node.lft
    )AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
    AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
    AND sub_parent.name = sub_tree.name
GROUP BY node.name
HAVING depth <= 1
ORDER BY node.lft