是否有可能做一个recursion的SQL查询?
我有一个类似这样的表格:
CREATE TABLE example ( id integer primary key, name char(200), parentid integer, value integer);
我可以使用parentid字段来将数据排列成一个树形结构。
现在,这是我无法解决的问题。 鉴于一个parentid,是否有可能编写一个SQL语句,将所有值字段加起来,并recursion树的分支?
更新:我使用posgreSQL所以花式MS-SQLfunction不可用。 无论如何,我希望这被视为一个通用的SQL问题。
顺便提一下,在提问的15分钟内有6个答案,我非常感动! 去堆栈溢出!
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);
以下是使用公用表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
上面的脚本创build了一个名为sumthis的“虚拟”表,它具有列id和val 。 它被定义为与union all合并的两个select的结果。
首先select获取根( where id = :selectedid )。
第二个select反复跟随前一个结果的子节点,直到没有任何东西可以返回。
最终结果可以像普通表一样处理。 在这种情况下val列被求和。
从版本8.4开始,PostgreSQL使用SQL标准WITH语法对公用表expression式进行recursion查询支持 。
如果您想要一个可以在任何ANSI SQL-92 RDBMS上工作的便携式解决scheme,则需要在表中添加一个新列。
Joe Celko是嵌套集方法的原始作者,用于在SQL中存储层次结构。 你可以通过谷歌的“嵌套集”层次来了解更多关于背景的信息。
或者你可以重新命名为leftid并添加一个rightid 。
下面是我总结嵌套集的尝试,因为我不是Joe Celko,所以这个集合会变得非常短暂:SQL是一个基于集合的语言,并且邻接模型(存储父ID)不是基于集合的层次结构表示。 因此,没有纯粹的基于集合的方法来查询邻接模式。
但是 ,大多数主要平台近年来都引入了扩展来处理这个确切的问题。 所以如果有人回复Postgres特定的解决scheme,一定要使用它。
在SQL中进行recursion查询的标准方法是recursionCTE 。 PostgreSQL从8.4支持它们。
在早期版本中,可以编写一个recursion设置返回函数:
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,那么使用公共表expression式来实现这一点非常酷。
它将创build一个临时表所需的所有工作都放在一边,基本上只需要一个WITH和一个UNION即可完成。
这是一个很好的教程:
http://searchwindevelopment.techtarget.com/tip/0,289483,sid8_gci1278207,00.html
使用公共表格expression式 。
可能只想表明这是SQL Server 2005或更高版本。 戴尔·拉根
这里有一篇关于没有公共表expression式的SqlTeamrecursion的文章 。
下面的代码编译并且testingOK。
创build或replace函数子树(bigint)
返回setof例如$$
宣布
结果logging;
录入logging
logging;
开始
从父母= $ 1的例子中select结果*
如果find的话
用于从父母= $ 1和孩子父母循环的例子中select子女
select * from子树(entry.child)循环中的recs
返回下一个recs;
末端循环;
末端循环;
万一;
返回下一个结果;
结束;
$$语言'plpgsql';
因为节点指向自己,所以在我的情况下需要条件“child <> parent”。
玩的开心 :)
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;
尽pipe这个问题已经得到了很好的回答,但应该指出的是,如果我们把这个问题看成是:
一般的SQL问题
那么SQL实现是非常直接的,因为SQL'99允许通过WITH RECURSIVE语句在规范中进行线性recursion(尽pipe我相信没有RDBMS完全实现这个标准)。 所以从理论的angular度来看,我们现在可以做到这一点。
没有一个例子对我有用,所以我修正了这个问题:
宣布
结果logging;
录入logging
logging;
开始
结果在select*从项目其中pid = $ 1循环
返回下一个结果;
select * from project_subtree(results.id)循环中的recs
返回下一个recs;
末端循环;
末端循环;
返回;
结束;
这是SQL Server吗? 难道你不能写一个循环的TSQL存储过程,并将结果联合在一起?
我也有兴趣,如果有这样做,但只有SQL的方式。 从我从地理数据库课上记得的一点,应该有。
我认为在SQL 2008中使用HierarchyID更容易
如果你需要存储任意的graphics,而不仅仅是层次结构,你可以将Postgres推到一边并尝试一个graphics数据库,比如AllegroGraph :
graphics数据库中的所有内容都以三元forms存储(源节点,边,目标节点),并为您提供一stream的支持来操作graphics结构并使用类似SQL的语言查询它。
它不能与Hibernate或Django ORM等很好的集成在一起,但是如果你对图结构(不仅仅是像Nested Set模型这样的层次结构)认真对待,
我也相信Oracle最终在他们的最新产品中增加了对真实graphics的支持,但是我惊讶地发现它花了这么长时间,很多问题都可以从这个模型中受益。