闭包表根节点使用数百万个节点查询性能

时间:2012-01-13 22:24:30

标签: sql sqlite hierarchical-data

我目前有一个用于分层数据的闭包表,它有500万个节点,在闭包表中产生约7500万行。使用SqLite,由于闭包表的大小,我的查询时间呈指数级增长。

CREATE TABLE `Closure` (`Ancestor` INTEGER NOT NULL ,`Descendant` INTEGER NOT NULL ,`Depth` INTEGER, PRIMARY KEY (`Ancestor`,`Descendant`) )
CREATE INDEX `Closure_AncestorDescendant` ON `Closure` (`Ancestor` ASC, `Descendant` ASC);
CREATE INDEX `Closure_DescendantAncestor` ON `Closure` (`Descendant` ASC, `Ancestor` ASC);
CREATE TABLE `Nodes` (`Node` INTEGER PRIMARY KEY NOT NULL, `Root` BOOLEAN NOT NULL, `Descendants` INTEGER NOT NULL);

即使只有大约5或6个节点符合查询,我查询这些节点的查询大约需要20分钟。

SELECT `Closure`.`Ancestor` FROM `Closure` 
LEFT OUTER JOIN `Closure` AS `Anc` ON `Anc`.`Descendant` = `Closure`.`Descendant` 
AND `Anc`.`Ancestor` <> `Closure`.`Ancestor` WHERE `Anc`.`Ancestor` IS NULL;

20分钟是很长的所以现在我正在存储一个bool,如果节点是根并且在节点移动时修改NodesRoot列。我不是对重复数据非常满意,但我的查询时间现在是每个查询的单位数毫秒。

我还有很多查询需要知道给定节点有多少后代(大多数情况下,如果后代&gt; 1知道这个对象是否可以在树视图中虚拟化/扩展)。我曾经在每次需要时查询这个问题,但是在一个巨大的数据库中,就像我甚至使用索引一样,查询似乎需要很长时间(超过1秒),所以我也将它们缩减为Nodes。{{1每次移动节点时我也会更新的列。不幸的是,这是我想避免的另一个重复数据。

我以前使用的查询如下所示。如果有人能解释如何提高性能(考虑到我已经有一个以Ancestor开头的索引),我将不胜感激。

Descendants

1 个答案:

答案 0 :(得分:3)

您正在开发的SQLite版本是否支持外键?如果是这样,您的闭包表设计应该有一个FK引用您使用闭包表支持的层次结构表。在TSQL中:

constraint fk_a FOREIGN KEY (ancestor) REFERENCES <hierarchy_tablename> (nodeid)
constraint fk_d FOREIGN KEY (descendant) REFERENCES <hierarchy_tablename> (nodeid)

抱歉,你必须查找相关的SQLite语法。

由于您已经在维护深度字段,即后代与其祖先之间的距离,因此您可以使用它来判断给定节点是否有子节点。

select top 1 'EXPANDABLE' as whatever
from closure C
where exists (select ancestor from closure where depth > 0 and ancestor = C.ancestor)
and ancestor = @Node

无论封闭表的大小如何,都应该相当快。如果从中得到一个空集,则不能再扩展给定节点,因为它没有子节点。一旦找到符合条件的实例,Exists就会返回true,并且您只获取前1名,因此您不会为关闭表中的每一行返回一行来传递@Node。

至于提高找到根的表现,请尝试以下内容。这是我用来查找根的东西,但我的闭包表只有大约200,000行。我比较了为每个生成的计划,并且您的代码使用了Hash,由于设备上的处理器要求,这可能会影响性能(我假设SQLite适用于iPhone / iPad或设备上的某种小型分发)。下面使用较少的处理能力和更多来自其计划中的索引的读取,并利用层次结构与闭包表的关系。我不能确定它会改善你的表现问题,但它值得一试。

select a.node_name, a.node_id
from test.hier a left outer join 
                 (select coo.descendant /* coo = CHILD OF OTHER */
                  from test.closure_tree coo right outer join test.closure_tree ro
                        on coo.ancestor <> ro.descendant /* ignore its self reference */
                        and coo.descendant = ro.descendant /* belongs to another node besides itself */)lo 
    on a.node_id = lo.descendant
where lo.descendant is null
相关问题