2011-11-20 88 views
9

假设我有一个包含父子关系的表。在单个SQL查询中查找多个父母的所有子女

 
parent child 
1  4 
1  5 
2  6 
3  7 
4  8 
6  9 
7  10 
8  11 

现在我有一个返回的人的名单查询(例如1和2),我想找到他们的孩子,孙子等(在这种情况下:4,5,6,8, 9,11)。

我知道我可以使用公用表表达式递归搜索,但我想知道是否可以创建一个SQL语句来一次查找所有后代,而无需遍历输入集。

编辑:抱歉不够清楚。我在寻找类似:

 
SELECT {Hierarchical relation} from table where parent in (1,2) 

这将导致与行的单一输出列4,5,6,8,9,11

我不再感兴趣,在输出中的关系,只是多个家庭的全套家庭成员。

+0

输出是否需要显示它们的关系(儿童,孙辈等)还是他们简单地下降? – UnhandledExcepSean

+0

你能给我们一个表格/行列格式的预期输出的样本,就像你如何用父子关系来展示表格一样吗? – Nonym

+0

Nonym,我将预期的输出添加到问题中。 – MvdD

回答

16

---- PlainTable ---- 
parent idElement (child) 
Null 1 
1  4 
1  5 
2  6 
3  7 
4  8 
6  9 
7  10 
8  11 

WITH tableR (parent, idElement) 
AS 
(
-- Anchor member definition 
    SELECT e.parent, e.idElement 
    FROM PlainTable AS e 
    WHERE parent in (1,2) 
    UNION ALL 
-- Recursive member definition 
    SELECT e.parent, e.idElement 
    FROM PlainTable AS e 
    INNER JOIN tableR AS d 
     ON e.parent = d.idElement 
) 
-- Statement that executes the CTE 
SELECT idElement 
FROM tableR --inner join to plain table by id if needed 
+0

此CTE确实按照我的意图搜索分层关系,但仅限于单个输入。看到我的编辑问题。 – MvdD

+0

CTE的第一个查询查找根节点,我假定根节点具有NULL作为父节点。昌河此查询为其他标准。 – danihp

+0

在SQL管理工作室中尝试您的查询。它查找父IS NULL的所有后代,它们是(1,4,5,8和11),然后从该结果集中仅返回1。正如我在上面提到的问题,输出应该是一个列(1,2,4,5,6,8,9,11),它们都是父母1和父母2的后代。 – MvdD

0

SQL Server 2008有内置的功能,方便分层数据:http://msdn.microsoft.com/en-us/magazine/cc794278.aspx

我知道我可以使用公用表表达式递归搜索,但我 想知道如果我可以创建一个SQL语句找出所有后代在 一次,而不必迭代输入集。

我不确定你的意思。大多数(也许都是?)CTE可以通过使用子查询来完成,但使用子查询不会更快。当你说你不想在输入集上“迭代”时,它听起来像是在谈论游标的使用,当然你可以做为基于集的操作(使用CTE或子查询),但是没有递归的方式。

编辑:对不起,我没有直接想......当然,你不能用普通的子查询进行递归,但即使你能做到这一点,它也不会更快。如果你想看看没有CTE的递归策略,那么尝试搜索类似'recursion sql 2000'的东西,因为CTE不在那时。以下是一些示例:http://www.mssqltips.com/sqlservertip/938/recursive-queries-with-sql-server-2000/。当然,你的问题的答案仍然是相同的。

0

在等待更新的帖子:

SELECT DISTINCT 
    p.parent AS parent 
, c.child AS child 
, IFNULL(g.child, 'NONE') AS grandchild_of_parent 
FROM parent_child as p 
LEFT JOIN parent_child AS c ON p.parent = c.parent 
LEFT JOIN parent_child AS g ON c.child = g.parent; 

结果是这样的:

parent child grandchild_of_parent 
1  4  8 
1  5  NONE 
2  6  9 
3  7  10 
4  8  11 
6  9  NONE 
7  10  NONE 
8  11  NONE 

这样一个头脑简单但很可能,更难维护的代码类型,但由于我不熟悉SQL Server 2008的内置功能来处理这种类型的请求,我只会抛出一个远射...

编辑:

只是让你可以看到自己的结果,而你学习common table expressions和/或可能pivots ...这将让您的结果,但只到1的曾孙和2

-- A. Parents 1 and 2 
SELECT DISTINCT p.parent FROM parent_child AS p 
WHERE p.parent IN (1,2) 
UNION 
-- B : Children of A 
SELECT DISTINCT p.child FROM parent_child AS p 
WHERE p.parent IN (1,2) 
UNION 
-- C : Children of B, Grandchildren of A 
SELECT DISTINCT p.child FROM parent_child AS p 
WHERE p.parent IN (
    SELECT DISTINCT p.child FROM parent_child AS p 
    WHERE p.parent IN (1,2) 
) 
UNION 
-- D : Children of C, Great-Grandchildren of A 
SELECT DISTINCT p.child FROM parent_child AS p 
WHERE p.parent IN (
    SELECT DISTINCT p.child FROM parent_child AS p 
    WHERE p.parent IN (
    SELECT DISTINCT p.child FROM parent_child AS p 
    WHERE p.parent IN (1,2) 
) 
) 

同样,我强烈建议你研究其他人发布的内容。并查看他们提供的链接。我给你提供的不雅问题不会持续很长时间 - >一旦你有了曾曾曾孙,这绝对是FAIL

+0

我正在寻找与所有相关ID的单个列。 – MvdD

+0

顺便说一下,IFNULL应该在上面的SQL Server查询中为ISNULL。 – MvdD

0

好吧,danihp的解决方案并把我在正确的轨道上。这是我想出了解决方案:

DECLARE @Input TABLE (
    id int 
) 

INSERT INTO @Input VALUES (1),(2) 

;WITH Relations (parent, child) 
AS 
(
    SELECT e.parent, e.child 
     FROM RelationTable AS e 
     WHERE parent in (SELECT * FROM @Input) 
    UNION ALL 
     SELECT e.parent, e.child 
     FROM RelationTable AS e 
     INNER JOIN Relations AS d 
      ON e.parent = d.child 
) 
SELECT child 
FROM Relations 

它导致孩子ID列表(不包括2个父IDS就像我刚才在提问时说): 4,5,6,8,9, 11

+0

哦!抱歉!我的错误,“(1,2)中的WHERE idElement”可能出现在第一个查询中,而不是最后一个!我会修好它。 – danihp

0
---- PlainTable ---- 
parent idElement (child_id) 
    Null 1 
    1  4 
    1  5 
    2  6 
    3  7 
    4  8 
    6  9 
    7  10 
    8  11 

**Table value function to get Child ids at 4(any) Level of parent in the same table:-** 

    FUNCTION fc_get_Child_IDs(Parent) 
    DECLARE @tbl TABLE (ID int) 
    DECLARE @level int=4 
    DECLARE @i int=1 
    insert into @tbl values (Parent) 
    while @i < @level 
    BEGIN 

    INSERT into @tbl 
    select child_id from PlainTable where Parent in (select ID from @tbl) and child_id not in (select ID from @tbl) 
    set @i = @i + 1 
    END