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

时间:2008-09-09 23:08:25

标签: sql postgresql recursive-query

我有一张类似的表:

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

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

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

UPDATE:我正在使用posgreSQL,因此我无法使用花哨的MS-SQL功能。无论如何,我希望将其视为一般的SQL问题。

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

14 个答案:

答案 0 :(得分: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的“虚拟”表,其中包含id列和val列。它被定义为两个选择与union all合并的结果。

首先select获取根(where id = :selectedid)。

第二个select迭代地跟踪前面结果的子项,直到没有任何东西可以返回。

然后可以像普通表一样处理最终结果。在这种情况下,val列被求和。

答案 1 :(得分:34)

从版本8.4开始,PostgreSQL使用SQL标准WITH语法对公用表表达式recursive query support

答案 2 :(得分:15)

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

Joe Celko是在SQL中存储层次结构的 Nested Sets 方法的原作者。您可以通过Google "nested sets" hierarchy了解有关背景的更多信息。

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

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

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

答案 3 :(得分:11)

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

答案 4 :(得分:10)

SQL中进行递归查询的标准方法是递归CTEPostgreSQL以来8.4支持他们。

在早期版本中,您可以编写一个递归的set-returns函数:

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)

参见这篇文章:

答案 5 :(得分:5)

使用common table expression

  

可能只想表明这是SQL Server 2005或更高版本。 Dale Ragan

here's an article在没有公用表表达式的情况下由SqlTeam递归。

答案 6 :(得分:5)

如果您使用的是SQL Server 2005,那么使用Common Table Expressions可以实现这一点。

创建一个临时表需要完成所有的工作,并且基本上允许你只使用WITH和UNION来完成所有工作。

这是一个很好的教程:

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

答案 7 :(得分:2)

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

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”,因为节点指向自己。

玩得开心:)

答案 8 :(得分: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

答案 9 :(得分:1)

虽然这个问题得到了很好的回答,但是应该注意的是,如果我们将其视为:

  

通用SQL问题

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

答案 10 :(得分:1)

没有一个例子适合我,所以我已经修好了这个:

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;

答案 11 :(得分:0)

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

我也很感兴趣,但是这样做只有SQL的方法。从我记得的地理数据库类中,应该有。

答案 12 :(得分:0)

我认为使用HierarchyID

在SQL 2008中更容易

答案 13 :(得分:-1)

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

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

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

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