Get family members

半腔热情 提交于 2019-12-04 04:26:34

The solution I found is not good at all. It gives the right answer but is very slow, even for this very small table.

 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

mehdi lotfi

In first I suggest you that use hierarchyid column for your table.

Try following query (without 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

This should work given any node. The core CTE's only work if you begin with a root child node. So the first part finds a root child if the starting person is not one. The technique is to walk up the hierarchy, then down, then back up to get everyone in a family.

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

I certainly don't think this is elegant, and I can't imagine it will perform well. I would be interested in knowing how well it performs on your volume of data. Not sure exactly how you are going to use this in production, but I recommend creating another field and populate it to identify entire families if this is something you have to do frequently.

Here is a simplified version of Nizam's July 23rd answer. With the PersonConn table properly indexed, I get very good results (however I have no way to test it with 200 million records). If a temp table were used instead of a table variable you could index ID, but I don't think this would improve performance much because the index would need updating after each insert.

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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!