在单个SQL查询中查找多个父项的所有子项

时间:2011-11-20 21:14:23

标签: sql sql-server hierarchical-data

假设我有一个包含父子关系的表。

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。

我不再对输出中的关系感兴趣,只是对多个家庭的完整家庭成员感兴趣。

7 个答案:

答案 0 :(得分:17)

这是

---- 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

答案 1 :(得分: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/。当然,你问题的答案仍然是一样的。

答案 2 :(得分:0)

使用 CTE

使用公用表表达式的递归查询

http://msdn.microsoft.com/en-us/library/ms186243.aspx

http://www.sqlservercentral.com/articles/T-SQL/65540/

可以帮到你。

答案 3 :(得分: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的曾孙。 / p>

-- 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

答案 4 :(得分:0)

通常我使用Nested Set Model来表示你可以让SQL为你做所有的工作,实际上你有SQL输出XML可以直接附加到.net treeview。希望这有帮助

链接:Is there a simple way to query the children of a node?

答案 5 :(得分: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个父ID): 4,5,6,8,9,11

答案 6 :(得分: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