如何构建数据库以实现快速节点访问
我正在寻找一种方法来构建数据库智能VirtualTreeView和SQLite数据库以快速检索数据。 使用VirtualTreeView有一个OnNodeInit事件,对于这个目的并不总是实用的。
数据是从Usenet新闻组获取的,需要进行线程化处理。 对线程有用的数据是post id(int64,也是主键),引用(引用线程中以前的帖子的字符串)。
该程序在引用中搜索字符串并确定它应该在哪个postid下进行。 因此,例如帖子ID = 1234,那么下一篇文章可能是1235,然后1236可能会回复到1234。
这是一个可能的数据库示例:
post id references parent id
1234 .... .... 0
1235 .... .... 0
1236 .... .... 1234
所以现在这就是它现在的样子。
现在,问题是如何构建这些数据以加快检索速度。 如果只有根节点,我可以根据数据库条目分配RootNodeCount,然后在OnNodeInit中根据请求逐个读取它们。 当有子节点时,我需要以某种方式重新排列数据库,以便知道如何根据打开的节点更快地获取子节点。
我正在考虑分配附加字段“has_subnodes”和后面的子节点的ID。 当一个节点被点击时,它会读取该节点和每个链接的节点。
你将如何组织这个数据库,以便它可以在OnNodeInit中很好地阅读,或者你会使用该事件吗? 节点也可以使用AddChildNoInit()方法启动。 任何想法或指针都会受到欢迎。
更新(以及我如何解决它)
这里提供了一些与非虚拟树视图相关的信息:在数据库中实现分层数据结构
我最终做的是使用Modified Preorder Tree Traversal将信息存储在有关节点的数据库中,并且每次首先请求某个节点:
a)它在内部缓存中查找,基本上与VirtualTreeView结构保持相同的结构。
b)如果在缓存中找到,则会删除此缓存条目(它永远不会保留超过100个条目)
c)如果未找到,则在缓存中添加100个项目(从请求节点起50个,向下50个)。 如果需要,这个数字当然可以修改为500或1000个项目。 还有一些额外的检查,看看它需要多少读/写,以避免读取太多重复的条目。
d)如果我需要更多速度,我可以根据用户滚动virtualtreeview的数量应用其他技术加载节点 - 类似于std :: vector如何分配内存 - 首先我只加载100个节点,然后如果用户滚动很多,我加载了200,然后加载了400等等......用户滚动的速度越快,它加载整个树,但如果他/她从不滚动,它仍然不会加载它。
这样,永远不会看到的节点永远不会从数据库加载。 它适用于使用鼠标滚轮进行滚动(当它通过缓存为空并且需要更多磁盘数据的时候,偶尔会出现短暂的延迟)以及使用箭头按钮/键进行滚动。 当您将滚动条拖动到特定位置(比如从底部到中部)时会稍微慢一点,但是由于无法立即从磁盘中获取数据,因此预计会出现这种情况。
如果我在加载缓存/项目之前预先确定要使用多少内存,最好的方法是滚动速度越快,但是当然如果从不显示数据,它会使用更多的内存。
您正在寻找将分层数据存储在数据库中。
问题在于SQL没有足够的能力处理这种数据。
你有很多解决方案,每个解决方案都有他们的缺点和优点。
如果您想了解每种方法,请点击以下链接:
http://www.sitepoint.com/hierarchical-data-database/
http://www.sitepoint.com/hierarchical-data-database-2/
我个人最喜欢的是Modified Preorder Tree Traversal
在这里,您以非常直观的方式将左右节点存储在数据库中,这使得节点的插入速度有点慢,但快速检索。
你可以在Delphi中编写你的逻辑,但我更喜欢在我选择的数据库中使用存储过程。
这样,你的Delphi逻辑保持简单,如果数据库更改你的Delphi代码不必。 如果你想要的话,我可以包含存储过程的SQL代码,但现在不行,因为那些代码不在我现在带在身边的笔记本电脑上。
不是最优雅的,但这是我用来填充我的树木的方法。
它只需要数据访问两个简单的查询,其余全部完成客户端。
它将轻松加载数以万计的节点。 (现在看它,我可能只用一个查询就能逃脱 - 它有点老了!):
procedure TFrameComponentViewer.LoadComponentTree;
var
RootNodeData : PMasterComponent;
CompQ,ParentQ : TMyQuery;
procedure PopulateNodeData(Node: PVirtualNode;ComponentID : integer);
var NodeData : PMasterComponent;
begin
if CompQ.Locate('ComponentID',ComponentID,[loCaseInsensitive]) then
begin
NodeData := TreeComponents.GetNodeData(Node);
//Populate your desired TreeData
NodeData.ComponentID := CompQ.Fields[fldComponentID].AsInteger;
NodeData.ComponentCode := CompQ.Fields[fldComponentCode].AsString;
NodeData.ComponentType := CompQ.Fields[fldComponentType].AsInteger;
NodeData.IsPipeline := CompQ.Fields[fldComponentIsPipeline].AsBoolean;
NodeData.Description := CompQ.Fields[fldComponentDescription].AsString;
NodeData.StartKP := CompQ.Fields[fldComponentStartKP].AsFloat;
NodeData.EndKP := CompQ.Fields[fldComponentEndKP].AsFloat;
NodeData.Diameter := CompQ.Fields[fldComponentDiameter].AsFloat;
NodeData.WallThickness := CompQ.Fields[fldComponentWallThickness].AsFloat;
NodeData.CriticalSpanLength := CompQ.Fields[fldComponentCSL].AsFloat;
NodeData.Historical := CompQ.Fields[fldComponentHistorical].AsBoolean;
end;
end;
procedure AddNodesRecursive(ParentNode : PVirtualNode;ParentNodeID : Integer);
var AddedNode : PVirtualNode;
AddedNodeData : PMasterComponent;
Children : Array of Integer;
i : Integer;
begin
try
ParentQ.Filtered := False;
ParentQ.Filter := 'Parent_ID = '+InttoStr(ParentNodeID);
ParentQ.Filtered := True;
ParentQ.First;
SetLength(Children,ParentQ.RecordCount);
for i:=0 to ParentQ.RecordCount-1 do
begin
Children[i] := ParentQ.Fields[0].AsInteger;
ParentQ.Next;
end;
for i:=0 to High(Children) do
begin
AddedNode := TreeComponents.AddChild(ParentNode);
AddedNodeData := TreeComponents.GetNodeData(AddedNode);
System.Initialize(AddedNodeData^); //initialize memory
PopulateNodeData(AddedNode,Children[i],CompQ);
AddNodesRecursive(AddedNode,AddedNodeData.ComponentID);
end;
finally
end;
end;
begin
TreeComponents.BeginUpdate;
treeComponents.Clear;
CompQ := TMyQuery.Create(nil);
ParentQ := TMyQuery.Create(nil);
try
CompQ.Connection := DataBaseline.BaseLineConnection;
CompQ.SQL.Add('SELECT * FROM Components');
CompQ.Open;
ParentQ.Connection := DataBaseline.BaseLineConnection;
ParentQ.Close;
ParentQ.SQL.Clear;
ParentQ.SQL.Add('SELECT ComponentID,Parent_ID FROM Components ORDER BY OrderNo');
ParentQ.Open;
RootNode := TreeComponents.AddChild(nil);
RootNodeData := TreeComponents.GetNodeData(RootNode);
System.Initialize(RootNodeData^); //initialize memory
RootNodeData.ComponentID := -1;
AddNodesRecursive(RootNode,-1);
finally
TreeComponents.EndUpdate;
TreeComponents.FullExpand;
CompQ.Close;
ParentQ.Close;
FreeandNil(CompQ);
FreeandNil(ParentQ);
end;
end;
注意: OrderBy
列是可选的,我需要它,因为我的树是特定于订单的。
所以数据库有这三列,加上你需要的任何自定义数据:
ID
, ParentID
(-1表示无父母), OrderNo