我有一张类似的表:
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个答案给我留下了非常深刻的印象!去堆栈溢出!答案 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中有几种方法可以做你需要的。
如果您可以安装模块,请查看tablefunc contrib。它有一个connectby()函数来处理遍历树。 http://www.postgresql.org/docs/8.3/interactive/tablefunc.html
同时查看ltree contrib,您可以调整您的表格使用:http://www.postgresql.org/docs/8.3/interactive/ltree.html
或者您可以使用PL / PGSQL函数自行遍历树。
这样的事情:
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
中进行递归查询的标准方法是递归CTE
。 PostgreSQL
以来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)
可能只想表明这是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;
答案 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的支持,但我很惊讶它花了这么长时间,很多问题都可以从这个模型中受益。