如何构建数据库以实现快速节点访问

我正在寻找一种方法来构建数据库智能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列是可选的,我需要它,因为我的树是特定于订单的。

所以数据库有这三列,加上你需要的任何自定义数据:

IDParentID (-1表示无父母), OrderNo

链接地址: http://www.djcxy.com/p/5967.html

上一篇: How to structure database for quick node access

下一篇: VirtualStringTree Correct/recommended use