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

Posted

技术标签:

【中文标题】SQL 查找树中的所有直系后代【英文标题】:SQL Find all direct descendants in a tree 【发布时间】:2009-07-17 18:03:52 【问题描述】:

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

我对表中数据的示例是:

编号 |姓名 |父母身份证 ---+--------------+------------ 0 |根 |空值 1 |节点 1 | 0 2 |节点 2 | 0 3 |节点 1.1 | 1 4 |节点 1.1.1 | 3 5 |节点 1.1.2 | 3

现在我想获取给定节点的所有直接后代的列表,但如果不存在,我希望它只返回节点本身。

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

孩子们 -------- 4 5

那么查询id=4的孩子为:

孩子们 -------- 4

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

【问题讨论】:

我知道如何使用游标或 CTE 来做到这一点,但那是 SQL Server。 【参考方案1】:

在新的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

详情请看我博客中的这篇文章:

PostgreSQL 8.4: preserving order for hierarchical query

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

并从此函数中选择:

SELECT  *
FROM    fn_hierarchy_connect_by(4, 1)

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

更多详情请参阅我博客中的这篇文章:

Hierarchical queries in PostgreSQL

更新:

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

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
        )

这比JOIN 更有效,因为第二个查询最多只进行两次索引扫描:第一个确保找出是否存在子行,第二个查询如果不存在则选择父行有孩子。

【讨论】:

这个问题是我希望它只显示第一级子节点,如果没有,只返回节点本身。在摆弄了更多之后,我给出了一个对我有用的答案。 +1 好答案!我目前正在努力解决这个问题,如果您能确认这仍然是您在最新 PostgreSQL 版本中解决问题的方式,我将不胜感激。此外,理想情况下,我正在寻找一种适用于 PosgreSQL 和 H2 的解决方案 @JacobusR:第一个查询是您应该在 9.2 中使用的内容 谢谢,只是为了让你知道,我读了你的博客。绝对棒!再次感谢。【参考方案2】:

找到了一个符合我要求的查询。

选择 * 从 ( SELECT id FROM t_tree WHERE name = '' ) 作为我, t_tree g 在哪里 ( ( (i.id = g.id) 和 不存在(SELECT * FROM t_tree WHERE parentid = i.id))或 ( ( (i.id = g.parentid) 和 EXISTS (SELECT * FROM t_tree WHERE parentid = i.id))

【讨论】:

以上是关于SQL 查找树中的所有直系后代的主要内容,如果未能解决你的问题,请参考以下文章

SQL 在自引用表中查找祖先/后代

构建 LINQ 表达式以查找与树节点的所有后代相关的项目

为啥我的直系后代选择器不起作用?

Dojo 中有没有办法在 DOM 元素中查找所有小部件后代?

二叉树中的查找操作(按值查找按位查找)

js DOM知识总结