5

データをすばやく取得するために、VirtualTreeViewおよびSQLiteデータベースを使用してデータベースを構造化する方法を探しています。VirtualTreeViewにはOnNodeInitイベントがありますが、この目的で常に実用的であるとは限りません。

データはUsenetニュースグループから取得され、スレッド化する必要があります。スレッド化に役立つデータは、投稿ID(int64、主キーでもあります)、参照(スレッド内の以前の投稿を参照する文字列)です。

プログラムは参照内の文字列を検索し、どのpostidの下に配置するかを決定します。たとえば、post id = 1234の場合、次の投稿は1235になり、1236は1234に返信する可能性があります。

考えられるデータベースの例を次に示します。

post id    references    parent id
  1234      .... ....       0
  1235      .... ....       0
  1236      .... ....      1234

だから今、これはそれが今どのように見えるかです。

さて、問題は、より高速な検索のためにこのデータをどのように構造化するかです。ルートノードしかない場合は、データベースエントリに基づいてRootNodeCountを割り当て、OnNodeInitで要求に応じて1つずつ読み取ります。サブノードがある場合は、データベースを再配置して、開いているノードに応じてサブノードを高速化する方法を認識できるようにする必要があります。

次のサブノードのIDで追加のフィールド「has_subnodes」を割り当てることを考えていました。ノードがクリックされると、そのノードとリンクされているすべてのノードが読み取られます。

OnNodeInitで適切に読み取れるように、このデータベースをどのように編成しますか、それともそのイベントを使用しますか?ノードは、AddChildNoInit()メソッドを使用して開始することもできます。任意のアイデアやポインタを歓迎します。

更新(そして私がそれをどのように解決したか)

ここでは、virtualtreeviewに関連しない情報をいくつか利用できます。 データベースに階層データ構造を実装する

私がやったことは、Modified Preorder Tree Traversalを使用して、ノードに関する情報をデータベースに格納し、特定のノードが最初に要求されるたびに実行することです。

a)基本的にVirtualTreeView構造と同じ構造を保持する内部キャッシュで検索されます。

b)キャッシュで見つかった場合、このキャッシュエントリは削除されます(100を超えるアイテムを保持することはありません)

c)見つからない場合は、さらに100個のアイテムがキャッシュに追加されます(要求されたノードから50個上、50個下)。もちろん、この数は必要に応じて500または1000アイテムに変更できます。重複するエントリを読みすぎないようにするために、読み取る必要のあるアップ/ダウンの量を確認するための追加のチェックがいくつかあります。

d)より高速が必要な場合は、追加の手法を適用できます-ユーザーがvirtualtreeviewをスクロールする量に基づいてデータベースからノードをロードします-std :: vectorがメモリを割り当てる方法と同様です-最初に100ノードのみをロードし、次にユーザーが大量にスクロールする場合、200、次に400などをロードします...ユーザーがスクロールするほど、ツリー全体のロードが速くなりますが、スクロールしない場合はロードされません。

このように、表示されないノードがデータベースからロードされることはありません。マウスホイールを使用したスクロール(キャッシュが空になり、ディスクからより多くのデータが必要になるポイントを通過するときに、時折短い遅延が発生する)や、矢印ボタン/キーを使用したスクロールには問題なく機能します。スクロールバーを特定の位置(たとえば、下から中央)にドラッグすると少し遅くなりますが、データをディスクからすぐにフェッチできないため、これは予想されます。

キャッシュ/アイテムをロードする前に、どれだけのメモリを使用するかを事前に決定しておくと、スクロールが速くなりますが、データが表示されない場合はもちろん、より多くのメモリを使用します。

4

2 に答える 2

2

最もエレガントではありませんが、これは私がツリーにデータを入力するために使用する方法です。

2つの単純なクエリのデータアクセスのみが必要であり、残りはすべてクライアント側で行われます。

何万ものノードを簡単にロードします。(今それを見ると、たぶん1つのクエリで逃げることができます-少し古いです!):

 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列はオプションです。ツリーは順序に固有であるため、この列が必要です。

したがって、DBには次の3つの列に加えて、必要なカスタムデータがあります。

IDParentID(親がない場合は-1)、OrderNo

于 2011-12-19T05:26:58.133 に答える
1

階層データをデータベースに保存しようとしています。
問題は、SQLがこの種のデータをうまく処理するための機能を備えていないことです。

あなたには多くの解決策があり、それぞれに短所と長所があります。
それぞれのアプローチについて詳しく知りたい場合は、次のリンクをご覧ください。

http://www.sitepoint.com/hierarchical-data-database/
http://www.sitepoint.com/hierarchical-data-database-2/

私の個人的なお気に入りはModified Preorder Tree Traversal

ここでは、非常に直感に反する方法でデータベースに左右のノードを格納します。これにより、ノードの挿入は少し遅くなりますが、取得は非常に高速になります。

Delphiでロジックをコーディングできますが、私は選択したデータベースでストアドプロシージャを使用することを好みます。
そうすれば、Delphiのロジックはシンプルに保たれ、データベースが変更された場合でもDelphiコードを変更する必要はありません。必要に応じて、ストアドプロシージャのSQLコードを含めることができますが、そのコードは私が今持っているラップトップにないため、今はできません。

于 2011-12-19T09:23:29.280 に答える