SQL查找树中的所有直接后代

tat*_*tsu 2 sql postgresql tree hierarchical-data

我的数据库中有一个使用父ID链接存储的树.

我对表中数据的一个示例是:

id  |  name      | parent id
---+-------------+-----------
0  | root          | NULL
1  | Node 1      | 0
2  | Node 2      | 0
3  | Node 1.1   | 1
4  | Node 1.1.1| 3
5  | Node 1.1.2| 3

现在我想得到一个给定节点的所有直接后代的列表,但如果不存在,我想让它只返回节点本身.

我希望id = 3的子查询的返回值为:

children
--------
4
5

然后查询id = 4的子节点:

children
--------
4

我可以改变我将树存储到嵌套集的方式,但我不知道这将如何使我想要的查询成为可能.

Qua*_*noi 6

在新的PostgreSQL 8.4你可以用CTE:

WITH RECURSIVE q AS
        (
        SELECT  h, 1 AS level, ARRAY[id] AS breadcrumb
        FROM    t_hierarchy h
        WHERE   parent = 0
        UNION ALL
        SELECT  hi, q.level + 1 AS level, breadcrumb || id
        FROM    q
        JOIN    t_hierarchy hi
        ON      hi.parent = (q.h).id
        )
SELECT  REPEAT('  ', level) || (q.h).id,
        (q.h).parent,
        (q.h).value,
        level,
        breadcrumb::VARCHAR AS path
FROM    q
ORDER BY
        breadcrumb
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅我的博客中的文章

8.3或之前,您必须编写一个函数:

CREATE TYPE tp_hierarchy AS (node t_hierarchy, level INT);

CREATE OR REPLACE FUNCTION fn_hierarchy_connect_by(INT, INT)
RETURNS SETOF tp_hierarchy
AS
$$
        SELECT  CASE
                WHEN node = 1 THEN
                        (t_hierarchy, $2)::tp_hierarchy
                ELSE
                        fn_hierarchy_connect_by((q.t_hierarchy).id, $2 + 1)
                END
        FROM    (
                SELECT  t_hierarchy, node
                FROM    (
                        SELECT  1 AS node
                        UNION ALL
                        SELECT  2
                        ) nodes,
                        t_hierarchy
                WHERE   parent = $1
                ORDER BY
                        id, node
                ) q;
$$
LANGUAGE 'sql';
Run Code Online (Sandbox Code Playgroud)

并从此功能中选择:

SELECT  *
FROM    fn_hierarchy_connect_by(4, 1)
Run Code Online (Sandbox Code Playgroud)

第一个参数是根id,第二个参数应该是1.

有关详细信息,请参阅我的博客中的这篇文章

更新:

要仅显示第一级子项,或者如果子项不存在则显示节点本身,请发出以下查询:

SELECT  *
FROM    t_hierarchy
WHERE   parent = @start
UNION ALL
SELECT  *
FROM    t_hierarchy
WHERE   id = @start
        AND NOT EXISTS
        (
        SELECT  NULL
        FROM    t_hierarchy
        WHERE   parent = @start
        )
Run Code Online (Sandbox Code Playgroud)

这比a更有效JOIN,因为第二个查询最多只能进行两次索引扫描:第一次确保查找是否存在子项,第二次查询是否存在子项时选择父行.