是否有可能做一个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的支持,但是我惊讶地发现它花了这么长时间,很多问题都可以从这个模型中受益。