我正在研究一个项目,这需要一个类别树,组织为id,父,标题表。在Postgres中,哪些是检索类别及其子类别(以及完整树,如果根类别的父类为0)的最佳方法?我正在寻找一个纯粹的数据库解决方案,但是如果有一种方法可以用于Ruby和PHP--它也会很棒。PostgreSQL - 树组织
主要目标是选择子句的速度,原因是此表中的数据对更新/插入/删除速度并不重要。
UPD:还会有路径搜索,我的意思是从当前顶点(类别)到根类别的路径。
我正在研究一个项目,这需要一个类别树,组织为id,父,标题表。在Postgres中,哪些是检索类别及其子类别(以及完整树,如果根类别的父类为0)的最佳方法?我正在寻找一个纯粹的数据库解决方案,但是如果有一种方法可以用于Ruby和PHP--它也会很棒。PostgreSQL - 树组织
主要目标是选择子句的速度,原因是此表中的数据对更新/插入/删除速度并不重要。
UPD:还会有路径搜索,我的意思是从当前顶点(类别)到根类别的路径。
检索类别及其子类别
如果你只有子项目的深度有限,您可以使用自联接,例如,做到这一点。两层深:
SELECT *
FROM categories AS child
LEFT JOIN categories AS parent ON parent.id=child.parent
LEFT JOIN categories AS grandparent ON grandparent.id=parent.parent
WHERE child.id=(id) OR parent.id=(id) OR grandparent.id=(id);
不能使用标准的SQL在“父ID的外键”型架构的任意深度的层次做到这一点。
一些DBMS提供了非标准的分层工具,以各种方式允许这样的事情,但是如果你想坚持跨DBMS兼容的代码,你需要将你的模式重新调整为更好的表示模型之一层次结构。两种流行的是:
Nested Set。存储线性排序,表示目标表的两列中的树的深度优先搜索(如果目标具有显式排序,则其中一个已经具有)。
。将每个祖先/后代对存储在单独的连接表中。
有优点和缺点每种方法,以及众多的变体(例如,稀疏组嵌套的编号在AR,“距离”),其可以影响多种类型添加/删除/移动位置的操作如何昂贵是。就个人而言,我倾向于默认使用简化的嵌套集模型,因为它包含的冗余比AR少。
查看"ltree" contrib模块。
我一直在玩ltree,这是一个PostgreSQL contrib模块,以查看它是否非常适合线程注释。创建你的表,用于存储路径列并在其上创建一个ltree指数..然后,您可以执行查询是这样的:
ltreetest=# select path from test where path ~ '*.Astronomy.*';
path
-----------------------------------------------
Top.Science.Astronomy
Top.Science.Astronomy.Astrophysics
Top.Science.Astronomy.Cosmology
Top.Collections.Pictures.Astronomy
Top.Collections.Pictures.Astronomy.Stars
Top.Collections.Pictures.Astronomy.Galaxies
Top.Collections.Pictures.Astronomy.Astronauts
我还没有与它周围玩足,以确定它如何执行像插入,更新或删除的东西。我想删除会是什么样子:
DELETE FROM test WHERE path ~ '*.Astronomy.*';
我想,螺纹评论表可能看起来像:
CREATE SEQUENCE comment_id_seq
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 78616
CACHE 1;
CREATE TABLE comments (
comment_id int PRIMARY KEY,
path ltree,
comment text
);
CREATE INDEX comments_path_idx ON comments USING gist (path);
的插入会粗暴(和未经考验的-LY)看起来像:
CREATE FUNCTION busted_add_comment(text the_comment, int parent_comment_id) RETURNS void AS
$BODY$
DECLARE
INT _new_comment_id; -- our new comment_id
TEXT _parent_path; -- the parent path
BEGIN
_new_comment_id := nextval('comment_id_seq'::regclass);
SELECT path INTO _parent_path FROM comments WHERE comment_id = parent_comment_id;
-- this is probably busted SQL, but you get the idea... this comment's path looks like
-- the.parent.path.US
--
-- eg (if parent_comment_id was 5 and our new comment_id is 43):
-- 3.5.43
INSERT INTO comments (comment_id, comment, path) VALUES (_new_comment_id, the_comment, CONCAT(_parent_path, '.', _new_comment_id));
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
什么的。基本上,路径只是由所有主键组成的层次结构。
有一个用于Rails的acts_as_tree
插件,它在过去对我很有帮助。尽管我有一棵相当小的树,大约有15,000个节点。
我很喜欢这种情况下的嵌套集模型。更新和插入可能有点棘手,但选择通常非常简洁和快速。如果添加一个实际的参考节点的父的性能会更好(这将消除在某些情况下加入。它还包括的childNodes的自然排序。
当前节点和一个典型的查询所有的孩子会是什么样子:
select name
from nestedSet c inner join nestedSet p ON c.lft BETWEEN p.lft AND p.rgt
where p.id = 1
order by lft
几个精心放置group by
条款也将净你关于你的一些树的快速统计
只需添加,文章Managing Hierarchical Data in MySQL就可以很好地解释相邻列表模型和嵌套集合模型,包括树操作等的示例SQL。
RDBMS中的层次结构是一个难题。我在我的愿望清单上有Joe Celko’s Trees and Hierarchies in SQL for Smarties可以在某天购买和阅读。
您可以*通过'parent-id-foreign-key'类型模式使用标准SQL对任意深度层次结构执行此操作:您可以使用递归公用表表达式。无可否认,PostgreSQL从8.4开始支持这个功能,在这个线程发布几个月后,我认为这可能是一个有用的附录。 FWIW,Firebird,MS SQL Server和DB2也支持递归CTE,尽管MSSQL的版本有限。 Oracle有自己奇怪的语法。 – 2010-08-24 07:36:45