是否可以进行递归 SQL 查询?

发布于 2024-07-04 04:44:55 字数 358 浏览 6 评论 0原文

我有一个与此类似的表:

CREATE TABLE example (
  id integer primary key,
  name char(200),
  parentid integer,
  value integer);

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

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

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

I have a table similar to this:

CREATE TABLE example (
  id integer primary key,
  name char(200),
  parentid integer,
  value integer);

I can use the parentid field to arrange data into a tree structure.

Now here's the bit I can't work out. Given a parentid, is it possible to write an SQL statement to add up all the value fields under that parentid and recurse down the branch of the tree ?

UPDATE: I'm using posgreSQL so the fancy MS-SQL features are not available to me. In any case, I'd like this to be treated as a generic SQL question.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

美人如玉 2024-07-11 04:44:55

SQL 中进行递归查询的标准方法是递归 CTEPostgreSQL8.4 开始支持它们。

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

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)

请参阅本文:

A standard way to make a recursive query in SQL are recursive CTE. PostgreSQL supports them since 8.4.

In earlier versions, you can write a recursive set-returning function:

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)

See this article:

没有伤那来痛 2024-07-11 04:44:55

以下代码可以编译并测试正常。

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

在我的例子中需要条件“child <>parent”,因为节点指向它们自己。

The following code compiles and it's tested OK.

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

The condition "child <> parent" is needed in my case because nodes point to themselves.

两相知 2024-07-11 04:44:55

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

Joe Celko 是嵌套集方法的原始作者在 SQL 中存储层次结构。 您可以通过 Google “嵌套集”层次结构了解有关的背景。

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

这是我尝试总结嵌套集的尝试,但它会严重不足,因为我不是 Joe Celko:SQL 是一种基于集合的语言,并且邻接模型(存储父 ID)不是层次结构的基于集合的表示。 因此,不存在纯粹的基于集合的方法来查询邻接模式。

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

If you want a portable solution that will work on any ANSI SQL-92 RDBMS, you will need to add a new column to your table.

Joe Celko is the original author of the Nested Sets approach to storing hierarchies in SQL. You can Google "nested sets" hierarchy to understand more about the background.

Or you can just rename parentid to leftid and add a rightid.

Here is my attempt to summarize Nested Sets, which will fall woefully short because I'm no Joe Celko: SQL is a set-based language, and the adjacency model (storing parent ID) is NOT a set-based representation of a hierarchy. Therefore there is no pure set-based method to query an adjacency schema.

However, most of the major platforms have introduced extensions in recent years to deal with this precise problem. So if someone replies with a Postgres-specific solution, use that by all means.

海之角 2024-07-11 04:44:55

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

Since version 8.4, PostgreSQL has recursive query support for common table expressions using the SQL standard WITH syntax.

人间☆小暴躁 2024-07-11 04:44:55

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

我也很感兴趣是否有一种仅 SQL 的方法可以做到这一点。 从我在地理数据库课上的记忆来看,应该有。

is this SQL Server? Couldn't you write a TSQL stored procedure that loops through and unions the results together?

I am also interested if there is a SQL-only way of doing this though. From the bits I remember from my geographic databases class, there should be.

治碍 2024-07-11 04:44:55

这些示例对我来说都不起作用,所以我像这样修复了它:

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;

None of the examples worked OK for me so I've fixed it like this:

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;
孤君无依 2024-07-11 04:44:55

顺便说一句,尽管这个问题已经得到很好的回答,但应该指出的是,如果我们将其视为:

通用 SQL 问题

,那么 SQL 实现相当简单,因为 SQL'99 允许通过 WITH RECURSIVE 语句在规范中进行线性递归(尽管我相信没有 RDBMS 完全实现该标准)。 所以从理论角度来看我们现在就可以做到这一点。

Just as a brief aside although the question has been answered very well, it should be noted that if we treat this as a:

generic SQL question

then the SQL implementation is fairly straight-forward, as SQL'99 allows linear recursion in the specification (although I believe no RDBMSs implement the standard fully) through the WITH RECURSIVE statement. So from a theoretical perspective we can do this right now.

淡墨 2024-07-11 04:44:55

Oracle 有“开始”和“连接”

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

Oracle has "START WITH" and "CONNECT BY"

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

高速公鹿 2024-07-11 04:44:55

下面是一个使用通用表表达式的示例脚本:

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 列被求和。

Here is an example script using common table expression:

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

The script above creates a 'virtual' table called sumthis that has columns id and val. It is defined as the result of two selects merged with union all.

First select gets the root (where id = :selectedid).

Second select follows the children of the previous results iteratively until there is nothing to return.

The end result can then be processed like a normal table. In this case the val column is summed.

小姐丶请自重 2024-07-11 04:44:55

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

像这样的事情:

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

There are a few ways to do what you need in PostgreSQL.

Something like this:

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);
执笏见 2024-07-11 04:44:55

如果您需要存储任意图形,而不仅仅是层次结构,您可以将 Postgres 推到一边并尝试图形数据库,例如 AllegroGraph

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

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

我还相信 Oracle 最终在其最新产品中添加了对真实图形的支持,但我很惊讶它花了这么长时间,很多问题都可以从这个模型中受益。

If you need to store arbitrary graphs, not just hierarchies, you could push Postgres to the side and try a graph database such as AllegroGraph:

Everything in the graph database is stored as a triple (source node, edge, target node) and it gives you first class support for manipulating the graph structure and querying it using a SQL like language.

It doesn't integrate well with something like Hibernate or Django ORM but if you are serious about graph structures (not just hierarchies like the Nested Set model gives you) check it out.

I also believe Oracle has finally added a support for real Graphs in their latest products, but I'm amazed it's taken so long, lots of problems could benefit from this model.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文