关于postgresql:是否可以进行递归SQL查询?

关于postgresql:是否可以进行递归SQL查询?

Is it possible to make a recursive SQL query?

我有一张与此类似的表:

1
2
3
4
5
CREATE TABLE example (
  id INTEGER PRIMARY KEY,
  name CHAR(200),
  parentid INTEGER,
  VALUE INTEGER);

我可以使用parentid字段将数据排列成树状结构。

现在这是我无法解决的问题。给定一个parentid,是否有可能编写一条SQL语句来添加该parentid下的所有值字段并向下递归该树的分支?

更新:我正在使用posgreSQL,因此无法使用精美的MS-SQL功能。无论如何,我希望将其视为通用SQL问题。

顺便说一句,我很高兴在提出问题的15分钟内能得到6个答案!去堆栈溢出!


以下是使用公用表表达式的示例脚本:

1
2
3
4
5
6
7
8
9
10
WITH recursive sumthis(id, val) AS (
    SELECT id, VALUE
    FROM example
    WHERE id = :selectedid
    UNION ALL
    SELECT C.id, C.value
    FROM sumthis P
    INNER JOIN example C ON P.id = C.parentid
)
SELECT SUM(val) FROM sumthis

上面的脚本创建一个名为sumthis的"虚拟"表,该表具有列idval。它定义为与union all合并的两个选择的结果。

第一个select获得根(where id = :selectedid)。

第二个select迭代地跟踪先前结果的子级,直到没有返回值为止。

然后可以像正常表一样处理最终结果。在这种情况下,将对val列求和。


从8.4版开始,PostgreSQL使用SQL标准WITH语法对通用表表达式提供了递归查询支持。


如果您想使用可在任何ANSI SQL-92 RDBMS上使用的便携式解决方案,则需要在表中添加一个新列。

Joe Celko是嵌套集方法在SQL中存储层次结构的原始作者。您可以使用Google"嵌套集"层次结构来了解有关背景的更多信息。

或者您可以将parentid重命名为leftid并添加一个rightid。

这是我尝试对嵌套集进行总结,因为我不是Joe Celko,嵌套集将严重不足:SQL是基于集合的语言,而邻接模型(存储父ID)不是基于集合的表示形式层次结构。因此,没有用于查询邻接模式的纯基于集合的方法。

但是,近年来,大多数主要平台都引入了扩展,以解决这一精确问题。因此,如果有人回复了Postgres特定的解决方案,请务必使用它。


在PostgreSQL中有几种方法可以满足您的需求。

  • 如果可以安装模块,请查看tablefunc组件。它具有一个处理遍历树的connectby()函数。 http://www.postgresql.org/docs/8.3/interactive/tablefunc.html

  • 还要检查ltree贡献,您可以修改表以使用:http://www.postgresql.org/docs/8.3/interactive/ltree.html

  • 或者您可以使用PL / PGSQL函数自己遍历树。

类似这样的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CREATE OR REPLACE FUNCTION example_subtree (INTEGER)
RETURNS setof example AS
'declare results record;
         child record;
 begin
  select into results * from example where parent_id = $1;
  if found then
    return next results;
    for child in select id from example
                  where parent_id = $1
      loop
        for temp in select * from example_subtree(child.id)
        loop
          return next temp;
        end loop;
      end loop;
  end if;
  return null;
end;'
LANGUAGE 'plpgsql';

SELECT SUM(VALUE) AS value_sum
  FROM example_subtree(1234);

SQL中进行递归查询的标准方法是递归CTE。从8.4开始,PostgreSQL支持它们。

在早期版本中,您可以编写递归集返回函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CREATE FUNCTION fn_hierarchy (parent INT)
RETURNS SETOF example
AS
$$
        SELECT  example
        FROM    example
        WHERE   id = $1
        UNION ALL
        SELECT  fn_hierarchy(id)
        FROM    example
        WHERE   parentid = $1
$$
LANGUAGE 'sql';

SELECT  *
FROM    fn_hierarchy(1)

查看本文:

  • PostgreSQL中的分层查询

如果您使用的是SQL Server 2005,则可以使用"公用表表达式"来实现此目的。

从创建临时表中消除了所有繁琐的工作,并且基本上允许您仅使用WITH和UNION来完成所有工作。

这是一个很好的教程:

http://searchwindevelopment.techtarget.com/tip/0,289483,sid8_gci1278207,00.html


使用公用表表达式。

May want to indicate this is SQL Server 2005 or above only. Dale Ragan

这是有关SqlTeam没有通用表表达式的递归的文章。


下面的代码可以编译,并且已经过测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CREATE OR REPLACE FUNCTION subtree (BIGINT)
RETURNS setof example AS $$
DECLARE
    results record;
    entry   record;
    recs    record;
BEGIN
    SELECT INTO results * FROM example WHERE parent = $1;
    IF found THEN
        FOR entry IN SELECT child FROM example WHERE parent = $1 AND child  parent loop
            FOR recs IN SELECT * FROM subtree(entry.child) loop
                RETURN NEXT recs;
            END loop;
        END loop;
    END IF;
    RETURN NEXT results;
END;
$$ LANGUAGE 'plpgsql';

在我的情况下,条件"子代<>父代"是必需的,因为节点指向自己。

玩得开心:)


这些示例都无法为我工作,因此我已将其修复为:

1
2
3
4
5
6
7
8
9
10
11
12
13
DECLARE
    results record;
    entry   record;
    recs    record;
BEGIN
    FOR results IN SELECT * FROM project WHERE pid = $1 loop
        RETURN NEXT results;
        FOR recs IN SELECT * FROM project_subtree(results.id) loop
            RETURN NEXT recs;
        END loop;
    END loop;
    RETURN;
END;

尽管问题已得到很好的回答,但作为简短说明,应该注意的是,如果我们将其视为:

generic SQL question

然后,SQL实现相当简单,因为SQL'99允许通过WITH RECURSIVE语句在规范中进行线性递归(尽管我相信没有RDBMS会完全实现该标准)。因此,从理论上讲,我们现在可以执行此操作。


Oracle具有" START WITH"和" CONNECT BY"

1
2
3
4
5
6
7
8
SELECT
    lpad(' ',2*(level-1)) || to_char(child) s

FROM
    test_connect_by

START WITH parent IS NULL
CONNECT BY prior child = parent;

http://www.adp-gmbh.ch/ora/sql/connect_by.html


我认为使用HierarchyID

在SQL 2008中更容易


这是SQL Server吗?您是否可以编写一个TSQL存储过程来循环并将结果合并在一起?

我也很感兴趣是否有一种仅使用SQL的方式来执行此操作。从我记得的地理数据库类的资料来看,应该有。


如果您需要存储任意图形,而不仅仅是层次结构,则可以将Postgres推到一边,然后尝试使用诸如AllegroGraph:

的图形数据库

图形数据库中的所有内容都存储为三元组(源节点,边,目标节点),它为您提供了一流的支持,可操纵图形结构并使用类似SQL的语言对其进行查询。

它不能与Hibernate或Django ORM之类的东西很好地集成在一起,但是如果您对图结构很认真(不仅像Nested Set模型这样的层次结构会给您),请检查一下它。

我也相信Oracle终于在其最新产品中增加了对真实图的支持,但是我很惊讶它花了这么长时间,该模型可能会带来很多问题。


推荐阅读

    linux查询网关命令?

    linux查询网关命令?,网络,信息,地址,环境,系统,网关,名字,中心,状态,命令,lin

    类似linux的命令编辑?

    类似linux的命令编辑?,环境,地址,命令,状态,工作,信息,情况,文件,目录,编辑,w

    linux命令查询参数?

    linux命令查询参数?,网络,信息,设备,系统,服务,状态,情况,工作,地址,命令,Lin

    linux查询硬盘命令行?

    linux查询硬盘命令行?,情况,系统,信息,工具,单位,软件,命令,服务,电脑,分析,

    linux查询分辨率命令?

    linux查询分辨率命令?,情况,设备,系统,分辨率,数据,命令,屏幕,屏幕分辨率,

    linux中递归压缩命令?

    linux中递归压缩命令?,系统,工作,命令,工具,管理,信息,设备,文件,目录,常用

    linux常用命令查询?

    linux常用命令查询?,工作,地址,系统,信息,命令,目录,管理,标准,常用命令,控

    linux命令查询汉语?

    linux命令查询汉语?,工作,地址,系统,信息,命令,目录,标准,状态,软件,亚洲,基

    linux下查询历史命令?

    linux下查询历史命令?,地址,信息,系统,服务,数据,连续,命令,数字,名称,环境,l

    linux查询表结构命令?

    linux查询表结构命令?,系统,标准,信息,数据,地址,设备,时间,适当,软件,命令,l

    linux网络命令查询?

    linux网络命令查询?,信息,网络,系统,地址,工作,状态,技术,电脑,命令,设备,lin

    linux查询子目录命令?

    linux查询子目录命令?,信息,系统,命令,名称,文件,名字,管理,软件,灵活,工具,L

    linux查询状态的命令?

    linux查询状态的命令?,系统,状态,信息,管理,数据,情况,命令,综合,电脑,工具,l

    linux地址查询命令?

    linux地址查询命令?,地址,网络,系统,工作,信息,设备,名称,标准,手机,管理,lin

    linux查询id命令?

    linux查询id命令?,信息,系统,密码,地址,用户,命令,数据,术语,用户名,用户信

    命令查询linux版本?

    命令查询linux版本?,系统,信息,名称,状态,电脑,设备,工具,软件,版本,命令,在L

    linux7查询路由命令?

    linux7查询路由命令?,网络,系统,信息,工作,地址,情况,命令,通信,工具,检测,在

    linux查询网卡命令?

    linux查询网卡命令?,地址,系统,网络,信息,电脑,服务,设备,技术指标,命令,状

    linux命令查询词典?

    linux命令查询词典?,地址,工作,标准,信息,系统,命令,管理,目录,单位,数据,lin

    linux隧道查询命令?

    linux隧道查询命令?,工作,系统,地址,信息,时间,网络,命令,检测,设备,目录,lin