63

我有一个类似的表:

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

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

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

更新:我正在使用 posgreSQL,所以我无法使用花哨的 MS-SQL 功能。无论如何,我希望这被视为一个通用的 SQL 问题。

顺便说一句,在提出问题的 15 分钟内有 6 个答案给我留下了深刻的印象!去堆栈溢出!

4

14 回答 14

42

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

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

于 2011-04-18T09:50:16.330 回答
34

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

于 2009-02-14T06:39:39.873 回答
15

如果您想要一个适用于任何 ANSI SQL-92 RDBMS的可移植解决方案,您需要向表中添加一个新列。

Joe Celko 是在 SQL 中存储层次结构的Nested Sets方法的原始作者。您可以谷歌“嵌套集”层次结构以了解有关背景的更多信息。

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

这是我对嵌套集的尝试总结,因为我不是 Joe Celko,所以它会很短:SQL 是一种基于集合的语言,而邻接模型(存储父 ID)不是基于集合的层次结构表示。因此,没有纯粹的基于集合的方法来查询邻接模式。

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

于 2008-09-09T23:40:43.020 回答
12

在 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);
于 2008-09-10T15:16:56.747 回答
10

进行递归查询的标准方法SQL是 recursive CTEPostgreSQL支持他们,因为8.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)

见这篇文章:

于 2011-01-11T16:11:19.793 回答
5

使用公用表表达式

可能想表明这只是 SQL Server 2005 或更高版本。戴尔·拉根

这是一篇关于没有公用表表达式的 SqlTeam 递归的文章。

于 2008-09-09T23:17:06.320 回答
5

如果您使用 SQL Server 2005,那么使用公用表表达式有一种非常酷的方法。

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

这是一个很好的教程:

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

于 2008-09-09T23:23:59.597 回答
2

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

创建或替换函数子树 (bigint)
返回 setof 示例为 $$
宣布
    结果记录;
    入境记录;
    记录记录;
开始
    select into results * from example where parent = $1;
    如果找到然后
        用于从父 = $1 和子父循环的示例中选择子项
            for recs in select * from subtree(entry.child) 循环
                返回下一个记录;
            结束循环;
        结束循环;
    万一;
    返回下一个结果;
结尾;
$$ 语言 'plpgsql';

在我的情况下需要条件“child <> parent”,因为节点指向自己。

玩得开心 :)

于 2009-02-03T21:14:11.583 回答
1

Oracle 有“START WITH”和“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

于 2008-09-09T23:30:11.330 回答
1

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

通用 SQL 问题

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

于 2009-03-17T22:38:46.390 回答
1

没有一个例子对我有用,所以我已经像这样修复它:

宣布
    结果记录;
    入境记录;
    记录记录;
开始
    对于 select * from project where pid = $1 循环中的结果
        返回下一个结果;
        for recs in select * from project_subtree(results.id) 循环
            返回下一个记录;
        结束循环;
    结束循环;
    返回;
结尾;
于 2011-01-11T16:03:10.220 回答
0

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

不过,我也很感兴趣是否有一种仅限 SQL 的方式来执行此操作。从我在地理数据库课上记得的部分来看,应该有。

于 2008-09-09T23:16:19.193 回答
0

我认为在 SQL 2008 中使用HierarchyID更容易

于 2008-09-09T23:23:47.757 回答
-1

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

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

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

我也相信甲骨文终于在他们的最新产品中增加了对真实图的支持,但我很惊讶它花了这么长时间,很多问题都可以从这个模型中受益。

于 2008-09-10T00:30:37.143 回答