得到家人

时间:2014-07-22 22:39:44

标签: sql sql-server sql-server-2012

劝说下面的家庭:

Family Tree

这个构建架构是:

create table PersonConn (child int, parent int)
insert into PersonConn values (1,2)
insert into PersonConn values (1,3)
insert into PersonConn values (5,3)
insert into PersonConn values (5,4)
insert into PersonConn values (6,7)
insert into PersonConn values (6,8)
insert into PersonConn values (2,9)
insert into PersonConn values (2,10)
insert into PersonConn values (3,11)
insert into PersonConn values (3,12)

要获得家庭成员的祖先,我可以使用递归,如下所示:

WITH Childs AS (
    SELECT distinct Child, Parent
    FROM  PersonConn
    WHERE Child = 1
    UNION ALL
    SELECT t2.Child, t2.Parent
    FROM   [Childs] t1
    INNER JOIN  PersonConn t2
        ON  t2.Child = t1.parent
)
SELECT PARENT FROM CHILDS

SQL Fiddle

它将占用所选成员的所有祖先(在此示例中为ID 1),但不是兄弟,例如。查询仅在族谱中上升。

我的问题是:

  

如何让所有家庭成员(儿子,父母,祖父,   叔叔,堂兄弟等......)从一个人开始?

更新

解决此问题的一种方法是将人员插入临时表中。在您可以将PersonConn表加入此临时表并插入其他人之后。这样做直到不再插入任何人。我正在寻找一种更有效(和优雅)的方式。 我在PersonConn表中有大约200MM的记录。

4 个答案:

答案 0 :(得分:1)

我发现的解决方案并不好。它给出了正确的答案,但即使对于这个非常小的桌子也很慢。

 DECLARE @INCLUIDOS TABLE (ID INT)

 INSERT INTO @INCLUIDOS VALUES(1)

 DECLARE @PAST_QUANT INT = 0
 DECLARE @QUANT INT = 1 

 WHILE @QUANT <> @PAST_QUANT
 BEGIN

     SET @PAST_QUANT = @QUANT

     INSERT INTO @INCLUIDOS
        SELECT PARENT 
        FROM PERSONCONN 
        WHERE CHILD IN (SELECT ID FROM @INCLUIDOS)
            AND PARENT NOT IN (SELECT ID FROM @INCLUIDOS)

    INSERT INTO @INCLUIDOS
        SELECT CHILD
        FROM PERSONCONN
        WHERE PARENT IN (SELECT ID FROM @INCLUIDOS)
            AND CHILD NOT IN (SELECT ID FROM @INCLUIDOS)

    SET @QUANT = (SELECT COUNT(*) FROM @INCLUIDOS)

END

SELECT DISTINCT ID FROM @INCLUIDOS

SQL Fiddle

答案 1 :(得分:1)

首先,我建议您使用hierarchyid列表。

尝试以下查询(不使用hierarchyid):

DECLARE @PersonId INT = 3

;WITH Parents AS (
    SELECT @PersonId AS Id
    UNION ALL
    SELECT child
    FROM PersonConn pc
    INNER JOIN Parents p ON pc.parent = p.Id
    ),
    Childs AS (
    SELECT distinct pc.Child, pc.Parent
    FROM  PersonConn pc
    INNER JOIN Parents p ON pc.child = p.Id OR pc.parent = p.Id
    UNION ALL
    SELECT t2.Child, t2.Parent
    FROM   [Childs] t1
    INNER JOIN  PersonConn t2
        ON  t2.Child = t1.parent
)
SELECT DISTINCT CASE WHEN N.n=1 THEN parent ELSE child END 
FROM CHILDS
CROSS APPLY(SELECT 1 UNION SELECT 2)N(n)

SQL Fiddle

答案 2 :(得分:0)

这应该适用于任何节点。核心CTE仅在您以root子节点开始时才有效。因此,如果起始人不是一个,则第一部分找到一个根子。该技术是走向层次结构,然后向下,然后向上回来以获得一个家庭中的每个人。

DECLARE @PersonId INT = 10

-- if id passed in is not a root child, then get one
If (SELECT Top 1 Parent FROM PersonConn WHERE Child = @PersonId) is null
   WITH CHILDS AS (
      SELECT Child, 0 as [level]
      FROM PersonConn
      WHERE Parent = @PersonId
      UNION ALL
      SELECT t2.Child, [level] + 1
      FROM CHILDS t1
      INNER JOIN  PersonConn t2
      ON  t2.Parent = t1.Child
      )
   SELECT Top 1 @PersonId = Child FROM CHILDS ORDER BY [level] Desc;

WITH CHILDS AS (
   SELECT Child, Parent
   FROM PersonConn
   WHERE Child = @PersonId
   UNION ALL
   SELECT t2.Child, t2.Parent
   FROM CHILDS t1
   INNER JOIN  PersonConn t2
   ON  t2.Child = t1.parent  
   ),
PARENTS AS (
   SELECT Child, Parent
   FROM PersonConn
   WHERE Parent in (Select parent from CHILDS) 
   UNION ALL
   SELECT t2.Child, t2.Parent
   FROM PARENTS t1
   INNER JOIN PersonConn t2
   ON t2.parent = t1.child
   ),
CHILDS2 AS (
   SELECT  Child, Parent
   FROM  PersonConn
   WHERE Child in(Select child From Parents)
   UNION ALL
   SELECT t2.Child, t2.Parent
   FROM   CHILDS2 t1
   INNER JOIN  PersonConn t2
   ON  t2.Child = t1.parent 
)
SELECT DISTINCT Parent, Child  FROM CHILDS2

我当然不认为这很优雅,我无法想象它会表现得很好。我很想知道它对你的数据量的表现如何。不确定你将如何在生产中使用它,但我建议创建另一个字段并填充它以识别整个家庭,如果这是你必须经常做的事情。

答案 3 :(得分:0)

以下是Nizam 7月23日答案的简化版。正确索引PersonConn表后,我得到了非常好的结果(但我无法用2亿条记录对其进行测试)。如果使用临时表而不是表变量,则可以将ID编入索引,但我不认为这会提高性能,因为索引在每次插入后都需要更新。

DECLARE @INCLUIDOS TABLE (ID INT)
INSERT INTO @INCLUIDOS VALUES(5)

WHILE @@ROWCOUNT <> 0
BEGIN

INSERT INTO @INCLUIDOS
    SELECT CHILD
    FROM PERSONCONN
    WHERE PARENT IN (SELECT ID FROM @INCLUIDOS)
        AND CHILD NOT IN (SELECT ID FROM @INCLUIDOS)

INSERT INTO @INCLUIDOS
    SELECT PARENT 
    FROM PERSONCONN 
    WHERE CHILD IN (SELECT ID FROM @INCLUIDOS)
        AND PARENT NOT IN (SELECT ID FROM @INCLUIDOS)
END

SELECT  ID FROM @INCLUIDOS