【问题标题】:Select Parent, Children, Children's Children, etc in single MySQL query在单个 MySQL 查询中选择 Parent、Children、Children's Children 等
【发布时间】:2012-10-26 05:29:50
【问题描述】:

我觉得这一定是个经典问题,但我找不到答案。

我有一个表格 Person,其中包含描述一个人的基本细节。然后,我有一个 ParentChildRelationship 表,看起来像这样:

CREATE TABLE `ParentChildRelationship` (
    `ParentId` INT(10) UNSIGNED NOT NULL,
    `ChildId` INT(10) UNSIGNED NOT NULL,
 PRIMARY KEY(ParentId,ChildId),
 CONSTRAINT `FK_ParentRelationship`
    FOREIGN KEY (`ParentId` )
    REFERENCES `Person` (`idPerson` ),
 CONSTRAINT `FK_ChildRelationship`
    FOREIGN KEY (`ChildId` )
    REFERENCES `Person` (`idPerson` )
);

我需要一个选择查询,它只返回树中父级和所有子级的所有人员记录。

例如,使用以下数据:

Parent   Child
1        3
1        8
2        4
3        5
3        6
6        9
4        7

选择 ParentId = 1 OR ChildId 在树中的所有 Person 记录下面 ParentId 为 1。此查询应返回 Person 信息 (SELECT * FROM Person...)以下 PersonId:

1,3,8,5,6,9

我不知道这是否重要,但是这些返回的顺序无关紧要,因为我需要根据“姓氏”之类的内容进行排序。换句话说,结果也可能是 1,3,5,6,9,8。

【问题讨论】:

    标签: mysql relationship


    【解决方案1】:

    如果您有一个已知的有限深度,您可以展开递归并使用存储过程或视图。对于 MySQL,以下工作:

    存储例程解决方案:

    DELIMITER $$ 
    
    CREATE PROCEDURE GetRelatedPersonsWithPersonId( IN pId VARCHAR(36)) 
            BEGIN 
                    select * from Person where idPerson in ( 
                            select ParentId from ParentChildRelationship where ParentId = pId 
                            union 
                            select ChildID from ParentChildRelationship where ParentId = pId 
                            union 
                            select ChildID from ParentChildRelationship where ParentId in (select ChildID from ParentChildRelationship where ParentId = pId) 
                            union 
                            select ChildID from ParentChildRelationship where ParentId in (select ChildID from ParentChildRelationship where ParentId in (select ChildID from ParentChildRelationship where ParentId = pId)) 
                    ) ; 
    
            END $$ 
    

    查看解决方案:

    Create view ChildRecurse 
    As 
    Select ParentId, ChildID from ParentChildRelationship 
    Union 
    Select x1.ParentId, x2.ChildId from ParentChildRelationship x1 
                   Inner join ParentChildRelationship x2 on x2.ParentId = x1.ChildId 
    Union 
    Select x1.ParentId, x3.ChildId from ParentChildRelationship x1 
                   Inner join ParentChildRelationship x2 on x2.ParentId = x1.ChildId 
                   Inner join ParentChildRelationship x3 on x3.ParentId = x2.ChildId 
    Union 
    Select x1.ParentId, x4.ChildId from ParentChildRelationship x1 
                   Inner join ParentChildRelationship x2 on x2.ParentId = x1.ChildId 
                   Inner join ParentChildRelationship x3 on x3.ParentId = x2.ChildId 
                   Inner join ParentChildRelationship x4 on x4.ParentId = x3.ChildId 
    

    然后这样选择:

    select * from person where idPerson=@ID or idPerson in (select ChildId from ChildRecurse where ParentId=@ID) 
    

    【讨论】:

    • 希望我可以将两个标记为答案(嵌套区间模型绝对很有趣),但是这个对于我的需要更实用。我现在可以人为地限制深度。
    【解决方案2】:

    您的数据模型称为adjacency list model。您无法使用该模型执行您所描述的查询(尽管存储过程可以)。

    解决此问题的常用方法是更改​​为nested set model。维基百科文章有一些示例代码,并且有一个很好的教程here(以及对使用邻接列表模型所涉及的内容的很好描述)。

    【讨论】:

    • 这很有帮助。我可能更喜欢使用存储过程手动迭代到嵌套集模型,但在走这条路之前,我将研究嵌套间隔模型。顺便说一句,这三个中哪一个是现实世界中这个问题的最常见解决方案?
    • @thephatp - this thread 您可能会感兴趣。我对嵌套区间模型没有任何经验,但粗略看看其他人写的内容表明它在正常使用模式下可以比嵌套集合模型具有更好的性能。
    【解决方案3】:

    我认为这将是 GROUP BY 和 HAVING 语句的工作。

    这样的事情是否为您指明了正确的方向?

    SELECT * FROM ParentChildRelationship pcr
    GROUP BY ParentID , ChildID
    HAVING ParentID=1 OR Count(ChildID)>0 
    

    这将为您提供一个父子关系列表,您可以使用 INNER JOIN 将其加入到persons 表中。

    【讨论】:

    • 这根本不能回答问题。 :( 这将返回所有父子关系的列表。我需要一个存在于单个顶部节点下的所有父子关系的列表。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-09
    相关资源
    最近更新 更多