我有一个评论树及其closure table:
create table comment (
id serial primary key,
author varchar(100) not null,
content text not null
);
create table closure (
ancestor integer not null references comment (id),
descendant integer not null references comment (id) on delete cascade,
depth integer not null,
primary key (ancestor, descendant)
);
我希望在ID为4
的评论下获取所有评论的子树。对评论主题进行广度优先遍历并不太难:
select comment.*, closure.depth
from comment
inner join closure on closure.descendant = comment.id
where ancestor = 4
order by depth asc;
如何对评论主题进行预订(深度优先)遍历?
(我意识到使用嵌套集进行预订遍历很容易,但我特别好奇如何使用闭包表。)
答案 0 :(得分:1)
首先,请不要先考虑广度或深度问题。理想情况下,您将把它看作一系列构建结果集的set操作。执行此操作的SQLy方法是执行类似于广度优先的操作,但一次只能在一个深度级别上操作。如果您尝试使用全宽度优先或全深度优先方法,则会遇到性能问题。
最佳方式
WITH RECURSIVE closure_tree AS (
SELECT descendent as id, ancestor as parent_id, 0 as depth, descendent::text as path
FROM closure
WHERE ancestor = 4
UNION ALL
SELECT c.descendent, ct.id, ct.depth + 1, ct.path || ',' || descendent::text
FROM closure c
JOIN closure_tree ct ON ct.id = c.ancestor
)
SELECT c.*, ct.depth, string_to_array(ct.path, ',')::int[]
FROM comment c
JOIN closure_tree ct ON c.id = ct.id
ORDER BY string_to_array(ct.path, ',')::int[];
未经测试,但这会给你一个想法。基本上,它会根据深度级别扫描表格(索引或顺序,具体取决于注释数量),在每次扫描时检索完整的图形宽度。请记住,SQL擅长管理集合,因此这是唯一能够做到这一点的理智方式。当然,这意味着闭包表应该只存储直接的父/子关系。
请注意,这会以深度优先的方式返回结果,而查询会首先以宽度返回结果。为了做到这一点,你需要以某种方式存储或生成路径,否则你无法获得真正的深度信息。所以你可以以非规范化的方式为你的闭包表添加路径,或者你可以抓住父/子关系并在查询中生成这个,如我的例子所示。
答案 1 :(得分:1)
我已经在我的ruby gem closure_tree中实现了预先排序的遍历,并且gem生成的SQL可以与MySQL和PostgreSQL一起使用。我相信我的实现是新颖的 - 但它非常多毛,需要两个选择,并且要求排序列为数字和[0-N],其中N是兄弟姐妹的数量,并且顺序没有间隙。