Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi TreeView-Nodes anhand Pfad-String finden (zu langsam) (https://www.delphipraxis.net/130241-treeview-nodes-anhand-pfad-string-finden-zu-langsam.html)

Satty67 5. Mär 2009 12:14


TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
Ich baue ein TreeView aus einer Datenquelle auf.
Die daten kommen mit zwei Informationen an: Kategorie und Dateninhalt

Die Kategorie ist wie ein Verzeichnis-Pfad aufgebaut: Kategorie\Subkategorie\Datenbeschreibung (bis Level 8 ) und wird damit passend in ein TreeView einsortiert. Leider kommen die Daten nicht nach Kategorie sortiert an, denn dann könnte ich den TreeView "on the fly" aufbauen.

Ich muss also ständig suchen, welcher Node zum PfadString passt, dort einsortieren oder neu anlegen. Dazu schneide ich den Pfad aus (Bsp. Kategorie\Subkategorie\) und übergebe den an die u.a. Funktion "FindNodeByPath". Je nach Rückgabe lege ich nur den Datennode an oder eben die fehlende Kategorien-Nodes.
Delphi-Quellcode:
(***************************************************************************
  Funktion, die aus einem Node (mit Parents) ein PathStr bildet
  Achtung! aNode-Pointer wird verändert
***************************************************************************)
function GetNodePath(aNode:TTreeNode; WithDelimiter: Boolean): AnsiString;
begin
  if WithDelimiter then Result := aNode.Text +'\'
    else Result := aNode.Text;

  while assigned(aNode.Parent) do begin
    aNode := aNode.Parent;
    Result := aNode.text + '\' + Result;
  end;
end;

(***************************************************************************
  Finden einen Node durch Angabe des Pfadnamen
  Achtung! aPath-Wert wird verändert
***************************************************************************)
function FindNodeByPath(const aTreeView: TTreeView; aPath: AnsiString): TTreeNode;
var
  i : Integer;
begin
  Result := NIL;
  if Assigned(aTreeView) and (Length(aPath)>0) then begin

    // Pfadstring UpperCase und Delimiter anfügen
    aPath := AnsiUpperCase(aPath);
    if aPath[length(aPath)] <> '\' then aPath := aPath +'\';

    // Alle Items aus dem TreeView testen
    for i := 0 to aTreeView.Items.Count-1 do begin
      // Vergleich, wenn positiv, dann Abbruch der Schleife
      if AnsiUpperCase(GetNodePath(aTreeView.Items[i], True)) = aPath then begin
        Result := aTreeView.Items[i];
        Break;
      end;
    end;

  end;
end;
Das funktioniert auch, wird aber proportional zu Datenmenge immer langsamer. Vor allem weil bei der Methode auch die irrelevanten Daten-Nodes mit geprüft werden.

Kann man das auch schneller lösen?

Meine Ideen bisher:

- Eine StringList parallel nur mit Ordnernamen und Node-Object, die ich dann via IndexOf durchsuche.
- oder Node.Data kennzeichnen, damit ich wenigstens die Daten-Nodes beim Stringvergleich ausklammern kann

Beide Ideen könnte ich selber umsetzen, aber nimmt mir die universelle Eigenschaft von "FindNodeByPath". Deshalb suche ich nach einer Idee, die innerhalb von "GetNodePath" oder "FindNodeByPath" verbessert, ohne jetzt spezielle Vorbereitung der TreeNodes zu fordern.

PS: VirtualTreeView hätte ich mir gerne mal angeschaut, scheint aber nicht für D5 zur Verfügung zu stehen.
PPS: String ist bei D5 noch ein AnsiString

himitsu 5. Mär 2009 12:27

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
anstatt zu jedem Node erstmal dessen Pfad zu bestimmen und dann zu vergleichen,

wäre es bestimmt schneller, wenn du den übergebenen Pfad zerlegst und dann diesen stückchenweise suchst.

hätte den Vorteil, daß im schlimmsten Fall nicht der ganze Baum durchsucht werden muß.

und du hättest gleich den ersten bereits existierenden Node-Teil
(am unteren Beispiel die Suche nach b\x\u\q ... man braucht also ab b\x\ anfangen den neuen Node einzubauen ... der Erste Teil exisitert ja schon)

z.B.
Code:
a
  d
    i
      m
  e
    j
b
  f
   
  g
    k
    l
      n
  x
c
  h
wenn man hier z.B. "b\x" möchte suchst du fast den gesamten Tree ab

Code:
zusammen  stückchenweise
a         a
a\d
a\d\i
a\d\i\m
a\e
a\e\j
b         b
b\f       b\f
b\g       b\g
b\g\k
b\g\l
b\g\k\l
b\x       b\x

PS:
Delphi-Quellcode:
  if WithDelimiter then Result := aNode.Text +'\'
    else Result := aNode.Text +'\';
irgendwie ist Result da immer das Selbe :angel2:

Satty67 5. Mär 2009 12:38

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
Also quasi erst Level 0 durchsuchen, dort dann bei passendem Namen weitermachen?
Klingt schonaml sehr gut, werde ich nachher versuchen umzusetzen...

€: Dein Edit verwiirt mich jetzt kurz, das muss ich nochmal ganz langsam lesen. Scheine ich richtig verstanden zu haben... erstmal auf Level 0 bleiben.

Zitat:

Zitat von himitsu
irgendwie ist Result da immer das Selbe :angel2:

Danke, verwende die Funktion auch zum Pfad generieren bei Daten-Node. Wäre da wohl nachher gestolpert (im Moment wurstel ich ja noch am Einlesen).

himitsu 5. Mär 2009 12:47

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
Zitat:

Zitat von Satty67
Also quasi erst Level 0 durchsuchen, dort dann bei passendem Namen weitermachen?

Scheine ich richtig verstanden zu haben... erstmal auf Level 0 bleiben.

genau so ^^

den linken Suchvorgang machst du ja jetzt schon
und beim rechten werden praktisch alle Subnodes ignoriert, die eh nicht passen

Satty67 5. Mär 2009 16:22

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
So, also ich bin auf eine Hürde gestossen:

Die Nodes kennen Ihre Sub-Nodes nicht. Keine Itemlisten wie z.B. beim MenuItem. Nur die globae Itemliste und eben Parent/HasChildren als einzige Zuordnung.

Ich kann also den passenden LvL-0 Node ermitteln, aber danach die Schleife nicht auf die Sub-Nodes beschränken. Den Vergleich natürlich schon, indem ich auf Parent prüfe. Dadurch durchlaufe ich aber die ItemListe gleich mehrmals komplett (je nach LvL-Tiefe).

Kann sein, das ich einen Denkfehler drin hab', dann bitte nochmal in die richtige Richtung schupsen ;)

***

Dein Ansatz war aber natürlich trotzdem ein richtiger Weg, nur hab' ich jetzt das Pferd von hinten aufgezäumt. Ich ermittel vom Schleifendurchlauf den LvL des gesuchten Nodes, indem ich die \ im Pfad zähle. Erst wenn der LvL passt, dann vergleiche ich jeweils den kompletten Pfad miteinander.

Könnte ich es wie oben beschrieben machen, würde sich bei jedem Fund die Datenmenge im Mittel halbieren, so ist es leider nicht ganz so effektiv. Das hat aber immerhin etwa 30% mehr Geschwindigkeit gebracht:
Delphi-Quellcode:
function FindNodeByPath(const aTreeView: TTreeView; aPath: String): TTreeNode;
var
  i, lvl : Integer;
  NodeText : String;
begin
  Result := NIL;
  if Assigned(aTreeView) and (Length(aPath)>0) then begin

    // Pfadstring UpperCase
    aPath := AnsiUpperCase(aPath);

    // Delimiter anfügen
    if aPath[Length(aPath)] <> '\' then aPath := aPath +'\';

    // Zählen welcher Level der Node haben müsste
    lvl := -1;
    for i := 1 to Length(aPath) do
      if aPath[i]='\' then inc(lvl);

    // Alle Items aus dem TreeView testen
    for i := 0 to aTreeView.Items.Count-1 do begin

      // Vorbedingungen testen
      if (aTreeView.Items[i].Level = lvl) then begin
        // Jetzt evtl. passenden Node-Pfad mit Gesuchtem vergleichen
        if AnsiUpperCase(GetNodePath(aTreeView.Items[i], True)) = aPath then begin
          Result := aTreeView.Items[i];
          Break;
        end;
      end;

    end;
  end;
end;
"GetNodePath" in die Schleife zu bauen (statt Funktionsaufruf) bringt übrigens keine 100ms. Auch nicht schneller, vorher neben LvL noch Node.Text zu vergleichen (das hatte ich auch schon mit drin).

Im ganzen werden übrigens 24.000 Nodes angelegt. Die erste Methode benötigte dazu noch >30.000ms. Die etwas optimierte nur noch 20.000ms. Die ganz andere Methode mit der String/Object-Liste und IndexOf ist allerdings immer noch um Längen schneller (<3.000ms).

Vielleicht hab' ich ja oben einen Denkfehler gemacht, indem ich himitsu's Vorschlag verworfen hab'. Aber sieht so aus, als ob die spezielle Datenstruktur des TreeView nicht mehr hergibt?

€: Gerade kommt mir etwas in den Sinn. Kommt der Eintrag "Kat\SubKat" immer nach dem Eintrag "Kat" in der ItemListe? Also ganz egal wie oft zuvor Nodes zugefügt und gelöscht worden sind? Wenn ja, müsste ich mir himitsu's Vorschlag nochmal vornehmen ;)

himitsu 5. Mär 2009 16:46

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
Zitat:

Die Nodes kennen Ihre Sub-Nodes nicht. Keine Itemlisten wie z.B. beim MenuItem. Nur die globae Itemliste und eben Parent/HasChildren als einzige Zuordnung.
Delphi-Quellcode:
var Node:  TTreeNode;
  NodeList: TTreeNodes;
  i: Integer;

begin
  NodeList := TreeView1.Items; // alle Items der Ebene 0
  i       := NodeList.Count;  // Anzahl der SubItems
  Node    := NodeList[0];     // erstes Item

  NodeList := Node.Item;       // alle SubItem
  i       := Node.Count       // Anzahl der SubItems
  Node    := Node.Item[0];    // erstes SubItem

RWarnecke 5. Mär 2009 16:55

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
Hallo Satty67,

ich habe einen CodeLib-Beitrag geschrieben, wie ich einen TreeView mit eine Datenbank mit zwei Tabellen dynamisch fülle. Vielleicht hilft Dir der Beitrag weiter. Ich habe das ganze mal bis Level 5 ausprobiert und es funktioniert.

Edit: Hier noch der Link aus der DP.

Satty67 5. Mär 2009 17:13

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
@himitsu:

NodeList := TreeView1.Items; // alle Items der Ebene 0

gibt alle Items des TreeView, aber beim Rest hast Du natürlich recht. Es steht auch so in der OH von D5. Wieso ich das wiedermal komplett übersehen hab' soll die Tage mein Therapeut klären.

@RWarnecke

Werde ich mir auf jeden Fall anschauen, auch wenn mich jetzt der Ehrgeiz gepackt hat, meinem bisher fabrizierten Mist selber zu beheben. Wenn ich das beim ersten überfliegen richtig sehe (worauf man sich bei mir wohl nicht verlassen kann), vergleichst Du nur Node.Text und brichst beim Ersten auftreten ab. Bei mir kommt Node.Text aber vielfach vor, nur eben in verschiedene Kategorien verteilt.

RWarnecke 5. Mär 2009 17:30

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
Zitat:

Zitat von Satty67
@RWarnecke
Werde ich mir auf jeden Fall anschauen, auch wenn mich jetzt der Ehrgeiz gepackt hat, meinem bisher fabrizierten Mist selber zu beheben. Wenn ich das beim ersten überfliegen richtig sehe (worauf man sich bei mir wohl nicht verlassen kann), vergleichst Du nur Node.Text und brichst beim Ersten auftreten ab. Bei mir kommt Node.Text aber vielfach vor, nur eben in verschiedene Kategorien verteilt.

Das ist nicht so schlimm, weil ich mir die ID zum Kategorienamen merke. Ich habe zum Beispeil folgenden Baumaufbau :
Zitat:

Hauptkategorie (ID = 1 / ParentID = 0)
|
|
------ Subkatgeorie (ID = 2 / ParentID = 1)
| |
| |
| ------- Subkategorie (ID = 3 / ParentID = 2)
|
|
------ Subkategorie (ID = 4 / ParentID = 1
Du kannst es ja ausprobieren, ob der Aufbau von Deinem TreeView funktioniert, indem Du Dir das Code-Orakel von mir runterlädst.

himitsu 5. Mär 2009 18:03

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
nicht getestet:
Delphi-Quellcode:
Function FindNodeByPath(aTreeView: TTreeView; Const aPath: String): TTreeNode;
  Var Path: Array of String;
    i: Integer;

  Begin
    Result := nil;
    Path  := Explode('\', ExcludeTrailingBackslash(aPath));
    If Path = nil Then Exit;
    Result := aTreeView.Items.GetFirstNode;
    i     := 0;
    While Assigned(Result) do Begin
      If Result.Text = Path[i] Then Begin
        If i < High(Path) Then Begin
          Inc(i);
          Result := Result.getFirstChild;
        End Else Exit;
      End Else Result := Result.getNextSibling;
    End;
  End;

Satty67 5. Mär 2009 18:09

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
^- Der Post kam während ich meinen geschrieben habe.
================================================== ==

Meine Daten sind aus einer alten Record-Datenbank. Die sind erst mal fix, aber die will ich ja später auf SQL exportieren, dann wird alles leichter. Ich werde deinen Source aber ganz sicher analysieren und wenn er passt auch verwenden. Aber ich muss jetzt, nachdem ich den Nachmittag dran gesessen bin, selber eine wenigstens brauchbare Version hinbekommen. Das kennst Du sicher ;)

***

Also ich weis jetzt auch, warum ich die Liste der SubNode nicht gefunden hatte. Node.Item <> TTReeNodes, sondern nur eine Objekt-Liste (was auch reicht), aber weshalb mir die Codevervollständigung nichts angeboten hatte. Ohne himitsu's var-Deklaration wäre ich nie drauf gekommen.

Dadurch musste ich die Schleife für TreeView.Items vom Konstrukt für Node.Item abspalten. Aber bin jetzt auf ~10.000ms für die 24.000 Node angekommen:
Delphi-Quellcode:
function FindNodeByPath(const aTreeView: TTreeView; aPath: String): TTreeNode;
var
  i : Integer;
  found : Boolean;
  NodeText : String;
begin
  Result := NIL;
  if Assigned(aTreeView) and (Length(aPath)>0) then begin

    // Pfadstring UpperCase und Delimiter anfügen
    aPath := AnsiUpperCase(aPath);
    if aPath[Length(aPath)] <> '\' then aPath := aPath +'\';

    // NodeText des obersten Level ausschneiden und Pfad kürzen
    NodeText := Copy(aPath,1,Pos('\',aPath)-1);
    Delete(aPath,1,Pos('\',aPath));

    // StartNode suchen
    for i := 0 to aTreeView.Items.Count-1 do
      if AnsiUpperCase(aTreeView.Items[i].Text) = NodeText then begin
        Result := aTreeView.Items[i];
        Break;
      end;

    // Wenn StartNode gefunden und noch eine Ebene existiert
    while (aPath <> '') and (Result <> NIL) do begin

      // Name der nächste Ebene, Pfad kürzen
      NodeText := Copy(aPath,1,Pos('\',aPath)-1);
      Delete(aPath,1,Pos('\',aPath));

      // Children durchsuchen
      found := false;
      for i := 0 to Result.Count do
        if AnsiUpperCase(Result[i].Text) = NodeText then begin
          Result := Result[i];
          found := True;
          Break;
        end;

      // Wenn nicht gefunden, dann Result verwerfen
      if not found then Result := NIL;

    end;
  end;
end;
Das ganze muss ich jetzt noch feintunen...

Satty67 5. Mär 2009 18:15

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
Delphi-Quellcode:
Path  := Explode('\', ExcludeTrailingBackslash(aPath));
Explode/ExcludeTrailingBackslash kennt D5 nicht,

himitsu 5. Mär 2009 18:22

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
schau mal, was deine OH alles kennt, was mit "Exclude" anfängt und dahinter irgendwas mit "Dir" oder "Path" stehen hat.
[add] eventuell ExcludeTrailingPathDelimiter [/add]

ExplodeExplode zerlegt einfach nur den String anhand von eines Trennzeichens in Einzelstrings.

Satty67 5. Mär 2009 18:28

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
ExcludeTrailingBackslash kennt D5, war nur Explode, das den Fehler verursacht hat.

Aber das Explode kann ich nachbauen. Das Trennzeichen ist noch dran oder in der Stringliste weggeschnitten?

Satty67 5. Mär 2009 18:38

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
Funktioniert!

Aber nur etwa 200ms schneller (die 10.000ms sind damit aber knapp geknackt):

Hier nochmal die D5 Variante:
Delphi-Quellcode:
Function FindNodeByPath(const aTreeView: TTreeView; aPath: String): TTreeNode;
Var
  Path: Array of String;
  i: Integer;
Begin
  Result := nil;
  if aPath='' then exit;

  //Path  := Explode('\', ExcludeTrailingBackslash(aPath));
  if aPath[Length(aPath)] <> '\' then aPath := aPath +'\';
  while aPath <> '' do begin
    i := Length(Path);
    SetLength(Path,i+1);
    Path[i] := Copy(aPath,1,Pos('\',aPath)-1);
    Delete(aPath,1,Pos('\',aPath));
  end;

  If Path = nil Then Exit;
  Result := aTreeView.Items.GetFirstNode;
  i     := 0;
  While Assigned(Result) do Begin
    If Result.Text = Path[i] Then Begin
      If i < High(Path) Then Begin
        Inc(i);
        Result := Result.getFirstChild;
      End Else Exit;
    End Else Result := Result.getNextSibling;
  End;
End;

RWarnecke 5. Mär 2009 18:49

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
Delphi-Quellcode:
type

  ArrOfStr = array of string;

implementation

function explode(sPart, sInput: string): ArrOfStr;
begin
  while Pos(sPart, sInput) <> 0 do
  begin
    SetLength(Result, Length(Result) + 1);
    Result[Length(Result) - 1] := Copy(sInput, 0,Pos(sPart, sInput) - 1);
    Delete(sInput, 1,Pos(sPart, sInput));
  end;
  SetLength(Result, Length(Result) + 1);
  Result[Length(Result) - 1] := sInput;
end;
erstellt durch Code-Orakel und dem BBCode-Plugin.
Das ist die Funktion, die himitsu meinte. Gibt es bei den Schweizern oder auch hier in der Code-Library.

himitsu 5. Mär 2009 18:55

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
nur 200? :shock:

selbst wenn .getFirstChild und .getNextSibling nicht optimal implementiert sind, hätt ich schon mehr erwartet



Nja, die TTreeView ist auch nicht unbedingt der Schnellste.
kennst du die VirtualTreeView?

Satty67 5. Mär 2009 19:06

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
@RWarnecke
Danke Dir. Den Code hab' ich in meine Tools-Unit übernommen. Auch wenn ich es ähnlich hinbekommen hatte, aber immer gut zu haben.

Zitat:

Zitat von himitsu
kennst du die VirtualTreeView?

Ja, darauf hoffe ich wenn ich auf Delphi 200x upgedated hab'. Leider ist VirtualTreeView erst ab D6. Hatte mal versucht den Code anzupassen, aber das war zuviel des guten. Auch mit ein Grund, warum das wirklich treue D5 doch langsam 2te Geige spielen muss.

TreeView ist wirklich lahm. Aber wirklich peinlich waren meine ersten Versuche mit TreeView (vor Jahren). Da musste ich Kaffee trinken gehen, wenn ich einen Baum aufgebaut habe. (BeginUpdate/EndUpdate kannte ich nicht)

Immerhin ist die Wartezeit jetzt von fast 31 Sekunden auf unter 10 Sekunden gefallen. Aus der alten Datenbank müssen ein paar Informationen gezogen werden, leider vergesse ich das Startproblem immer und beende das Programm (um es kurz danach wieder zu starten).

Mit ZEOS/Firebird mache ich sehr gute Fortschritte, je mehr man weis, desto einfacher wird es. Dann portiere ich die Datenbank nach SQL und kann RWarnecke Ideen/Code aus Code-Orakel zur Optimierung verwenden.

PS: @himistu

Der Fairness wegen muss noch erwähnt werden: Deine Version ist übrigens 1400ms schneller, nicht nur 200ms. Zuvor hatte ich bei meiner noch den "StringList-Cheat" aktiv.

RWarnecke 6. Mär 2009 07:15

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
Hallo Satty67,

hattest Du es mal mit meiner Variante ausprobiert ? Wäre mal interessant, wie schnell meine Variante ist, vielleicht kann man da ja noch etwas optimieren.

Chemiker 6. Mär 2009 08:14

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
Hallo Satty67,

vielleicht bringt es noch was den Text-Vergleich mit:

Delphi-Quellcode:
CompareText
durchzuführen.

Bis bald Chemiker

Satty67 6. Mär 2009 08:25

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
Du nutzt ja die Fähigkeiten von SQL, um Dir erst eine Liste zu erstellen, mit der Du dann optimiert arbeiten kannst (richtig?) Die Daten hier liegen aber auf einem alten Werkstatt-Rechner, der kein SQL hat. (Daten-Header werden per TFileStream seriell eingelesen, Tree angelegt und RecNo im Node für späteren Detailzugriff gespeichert).

Bin auch etwas überfordert, mir die ganze Funktion ohne SQL vorzustellen. Weis also im Moment nicht, wie ich die Hilfslisten ohne SQL erzeugen könnte ohne dadurch den Zeitvorteil wieder zu verlieren. Mein Versuch mit String/Object Liste für jeden Node war evtl. so ein Ansatz... da ich aber gleich eine universelle Funktion wollte, hab ich jede Vorbereitung der Daten verworfen.

Das ganze ist auch nur als Zwischenlösung gedacht, um die Daten bei mir in Delphi zur Verfügung zu haben. Später wird das nach SQL konvertiert, aber das dauert noch etwas.

€: Ist ja schon Freitag... werde mich am WE man mit 'ner Kanne Kaffee dransetzen um die Funktionsweise ganz genau zu verstehen und prüfen, wie das ohne SQL umzusetzen ist.

@Chemiker
werde ich heute Mittag schnell probieren und berichten. (das Projekt liegt auf dem Home-PC)

RWarnecke 6. Mär 2009 08:33

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
Zitat:

Zitat von Satty67
Du nutzt ja die Fähigkeiten von SQL, um Dir erst eine Liste zu erstellen, mit der Du dann optimiert arbeiten kannst (richtig?) Die Daten hier liegen aber auf einem alten Werkstatt-Rechner, der kein SQL hat. (Daten-Header werden per TFileStream seriell eingelesen, Tree angelegt und RecNo im Node für späteren Detailzugriff gespeichert).

Ja, dass ist richtig. Mit dem Select-Befehl erzeuge ich mir eine gesamte Tabelle aus der Kategorie und den Einträgen in den Kategorien.
Zitat:

Zitat von Satty67
Das ganze ist auch nur als Zwischenlösung gedacht, um die Daten bei mir in Delphi zur Verfügung zu haben. Später wird das nach SQL konvertiert, aber das dauert noch etwas.

Warum dieser umständliche Weg ? Ich würde versuchen die Daten auszulesen und gleich in eine Datenbank zu schreiben und dann danach den TreeView aufzubauen.

himitsu 6. Mär 2009 08:40

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
Zitat:

Zitat von Satty67
Du nutzt ja die Fähigkeiten von SQL, um Dir erst eine Liste zu erstellen, mit der Du dann optimiert arbeiten kannst (richtig?) Die Daten hier liegen aber auf einem alten Werkstatt-Rechner, der kein SQL hat. (Daten-Header werden per TFileStream seriell eingelesen, Tree angelegt und RecNo im Node für späteren Detailzugriff gespeichert).

werde die Daten in den RAM geladen und bleiben dan da? (z.B. in Recotrd oder Klassen)

dann lönntest du diese Records auch direkt an den jeweiligen TreeNode anhängen.

Zitat:

Zitat von Chemiker
vielleicht bringt es noch was den Text-Vergleich mit Delphi-Referenz durchsuchenCompareText durchzuführen.

eigentlich dürfte das langsamer sein, als der direkte Stringvergleich, da CompareText erstmal den beide Strings ändert (Groß-/Kleinschreibung) und erst danach vergleicht.
(bringt nur den Vorteil, daß es CaseInsensitiv vergleicht)

Satty67 6. Mär 2009 09:24

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
Zitat:

Zitat von himitsu
werde die Daten in den RAM geladen und bleiben dan da? (z.B. in Recotrd oder Klassen)
dann lönntest du diese Records auch direkt an den jeweiligen TreeNode anhängen.

Meine allerste Version hat die komplette Datenbank in den Speicher gelesen, Index sortiert und danach "on the fly" in den TreeView eingebaut. Das war fix und unschlagbar schnell, da suchen nach Nodes komplett entfallen ist (Child kam immer nach Parent). Für das aktuelle Programm könnte ich auch bei der Lösung bleiben (auch wenn der Werkstattrechner mit Speicher etwas mager ausgestattet ist). Mein Anliegen war ja jetzt eine möglichst universelle Version, also Optimierung für den Einzelfall bitte abhaken ;)

Es geht also schon in erster Linie darum, eine Funktion zu haben, die nur TTreeView und einen PfadString bekommt und alleine damit arbeiten muss. Die Pfadstrings sollen dabei auch in unsortierter Reihenfolge ankommen dürfen.

Wenn ich jetzt also bedenke, das ein lineares anlegen der 24.000 Nodes ~3500ms dauert, das wahllose anlegen mit String-Vergleich 9500ms (himitsus letzter Vorschag), dann könnte es gut sein, das wir nahe am Optimum sind. Immerhin sind wir von über 31.000ms auf 9.500ms runtergekommen.

***

Was eine spätere SQL-version angeht... wenn ich die Datenbank nach Pfad sortieren lasse, dann muss ich überhaupt keinen Node suchen. Es kommt in der Pfadliste immer Parent vor Child bzw. neuer Parent-Zweig. Ich muss also nur prüfen, ob der letzte Pfad zum nächsten Pfad passt.

Hybrid666 6. Mär 2009 09:27

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
bau dir doch erstmal einen baum auf, aus diesem baum machst du dann den treeview. wäre jetzt eine überlegung von mir.

also baum sieht dann aus:

Wurzelverzeichniss
| |
untervrz. untervrz.
| | | |
uuvz uuvz uuvz uuvz

da kannst du sehr schnell suchen und einfügen, wenn du dann alle daten beieinander hast kannst du den treeview aufbauen. geht mittels dem baum auch recht flott dann...


Andere Idee:

In einer Liste Referenzen zu den Nodes speichern, dann anstatt in der TreeView zu suchen in der liste suchen und dann direkt einfügen.

Sieht dann so aus:
/ -> TTreeNode;
/asd -> TTreeNode;
/bla -> TTreeNode;
/asd/möp -> TTreeNode;

wenn du nun /asd/möp/blubb einfügen möchtest, suchst du in der liste nach /asd/möp und speicherst blubb als kind knoten, dannach fügst du es der liste hinzu, damit
/asd/möp/blubb -> TTreeNode; auch drinsteht.

mfG

P.S.: Bitte nicht hauen wenn Idee kagga :D

mfG

Satty67 6. Mär 2009 22:39

Re: TreeView-Nodes anhand Pfad-String finden (zu langsam)
 
Also hab' heute noch was gefunden, was den Aufbau doppelt so schnell ausführt. (da hatte ich gepennt)

Zwar sind viele Einträge durcheinander, aber doch nicht alle. Ich prüfe vorm Aufruf von "FindNodeByPath" jetzt, ob AktuellerPad=LetzterPfad und nehme dann den zuletzt gefundenen Node. (Quasi eine 1-Wert-Referenzliste)

Könnte man auch innerhalb der Funktion implementieren, aber wegen der 3 nötigen globalen Variablen (LastTreeView, LastNode und LastPath) lasse ich es im Aufrufer. Sind jetzt 4,5 Sekunden für 24.000 Nodes, mir reicht das und ich kümmer mich jetzt lieber um den restlichen Code.

Die erste Variante von mir läge dann bei ca 15 Sekunden, womit weiterhin >60% mehr Geschwindigkeit durch Eure Mithilfe rausgesprungen sind!

Danke!


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:02 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz