AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Algorithmen, Datenstrukturen und Klassendesign Delphi Frage an die Experten für Sortier-Algo-Optimierung
Thema durchsuchen
Ansicht
Themen-Optionen

Frage an die Experten für Sortier-Algo-Optimierung

Ein Thema von Codehunter · begonnen am 30. Sep 2013 · letzter Beitrag vom 30. Sep 2013
Antwort Antwort
Benutzerbild von Codehunter
Codehunter

Registriert seit: 3. Jun 2003
Ort: Thüringen
2.272 Beiträge
 
Delphi 10.4 Sydney
 
#1

Frage an die Experten für Sortier-Algo-Optimierung

  Alt 30. Sep 2013, 09:47
Moin!

Ich habe eine Datenbanktabelle mit ca. 1600 Records. Jeder Record hat u.A. eine ID und eine ParentID. Letztere ist ein Verweis auf die ID eines anderen Datensatzes (beides Integer-Werte). Die Daten füttern einen VirtualTreeView. Hier mein bisheriger Code, der zwar ganz exakt das tut was er soll (die Baumstruktur anhand von ID und ParentID bilden), arbeitet aber gaaaanz schröcklich langsam.

Die jetzige Routine arbeitet so, dass zunächst der Baum als flache Liste gefüllt (das geht noch pfeilschnell) und dann über den Sortier-Algo (unterhalb des finally in der Prozedur LoadPlacesTree) die Nodes ihrem jeweiligen Parent "untergeschoben" werden. Das Problem dabei: Innerhalb des Algo laufen zwei verschachtelte Schleifen (Hauptschleife und die in GetPlaceParentNode), sodass im Extremfall 2,56 Mio Durchläufe und Integer-Vergleiche ablaufen.

Idealerweise müsste man den Baum bereits beim Auslesen der Daten aus qryPlacesList aufbauen. Nur kann ich nicht davon ausgehen, dass die Reihenfolge der Datensätze dazu führt, dass jeder ParentNode-Datensatz vor den betreffenden ChildNode-Datensätzen in der Datenmenge liegt. Auch eine Vorsortierung über ein ORDER BY ist nicht möglich, da sich die IDs und ParentIDs aus den Autoinc-Werten bei der Datensatzerstellung bilden und der User später die Sortierung der Nodes frei ändern kann (wobei die IDs erhalten bleiben und sich nur die ParentIDs entsprechend ändern). Es kann also durchaus vorkommen, dass die ParentID größer ist als die ID eines Datensatzes.

Ich wäre euch für Anregungen zur Optimierung sehr dankbar.
Delphi-Quellcode:
type
  PPlaceRec = ^TPlaceRec;
  TPlaceRec = record
    Id: Integer;
    ParentId: Integer;
    Name: string;
    TypeId: Integer;
    TypeName: string;
    AllowedChildTypes: string;
  end;

{...}

function TfrmMain.GetPlaceFromNode(ANode: PVirtualNode): PPlaceRec;
begin
  Result:= tvPlaces.GetNodeData(ANode);
end;

function TfrmMain.GetPlaceParentNode(APlace: PPlaceRec): PVirtualNode;
var
  Node: PVirtualNode;
  Place: PPlaceRec;
begin
  Result:= NIL;
  if APlace^.ParentId = 0 then Exit;

  Node:= tvPlaces.GetFirst;
  while Node <> NIL do begin
    Place:= PlaceFromNode[Node];
    if Place <> NIL then begin
      if Place^.Id = APlace^.ParentId then begin
        Result:= Node;
        Break;
      end;
    end;
    Node:= tvPlaces.GetNext(Node);
  end;
end;

{...}

procedure TfrmMain.LoadPlacesTree;
var
  Node, ParentNode: PVirtualNode;
  Place: PPlaceRec;
begin
  with qryPlacesList do begin
    if not Active then Active:= TRUE;
    if RecordCount > 0 then begin
      First;
      tvPlaces.BeginUpdate;
      try
        while not Eof do begin
          Node:= tvPlaces.AddChild(NIL);
          if Assigned(Node) then begin
            Place:= tvPlaces.GetNodeData(Node);
            if Assigned(Place) then begin
              with Place^ do begin
                Id:= Fields.FieldByName('id').AsInteger;
                ParentId:= Fields.FieldByName('parent_id').AsInteger;
                Name:= Fields.FieldByName('name').AsString;
                TypeId:= Fields.FieldByName('type_id').AsInteger;
                AllowedChildTypes:= Fields.FieldByName('allowed_child_types').AsString;
              end;
            end;
            Next;
          end;
        end;
      finally
        Node:= tvPlaces.GetFirst;
        while Node <> NIL do begin
          Place:= GetPlaceFromNode(Node);
          if Place <> NIL then begin
            ParentNode:= GetPlaceParentNode(Place);
            if Node.Parent = ParentNode then begin
              Node:= tvPlaces.GetNext(Node);
              Continue;
            end;
            if ParentNode <> NIL then begin
              tvPlaces.MoveTo(Node, ParentNode, amAddChildLast, FALSE);
              Node:= tvPlaces.GetNext(ParentNode);
            end else begin
              Node:= tvPlaces.GetNext(Node);
            end;
          end else begin
            Node:= tvPlaces.GetNext(Node);
          end;
        end;
        tvPlaces.FocusedNode:= NIL;
        tvPlaces.EndUpdate;
      end;
    end;
    Active:= FALSE;
  end;
end;
Ich mache grundsätzlich keine Screenshots. Schießen auf Bildschirme gibt nämlich hässliche Pixelfehler und schadet der Gesundheit vom Kollegen gegenüber. I und E zu vertauschen hätte den selben negativen Effekt, würde aber eher dem Betriebsklima schaden
  Mit Zitat antworten Zitat
Benutzerbild von sx2008
sx2008

Registriert seit: 15. Feb 2008
Ort: Baden-Württemberg
2.332 Beiträge
 
Delphi 2007 Professional
 
#2

AW: Frage an die Experten für Sortier-Algo-Optimierung

  Alt 30. Sep 2013, 10:08
Hätte die Tabelle das Feld "Level" (0=Root-Knoten, 1=Konten auf nächst tieferer Ebene, ...) dann könnte man nach diesem Feld sortieren.
Ob es sinnvoll ist dieses Feld einzuführen hängt insbesondere davon ab ob der Baum relativ statisch ist; also ob die Knoten ihren Level für immer behalten oder ob "in der Mitte" Konten gelöscht oder eingefügt werden.
fork me on Github
  Mit Zitat antworten Zitat
ManBu

Registriert seit: 4. Mär 2008
9 Beiträge
 
#3

AW: Frage an die Experten für Sortier-Algo-Optimierung

  Alt 30. Sep 2013, 10:12
Du könntest bereits gefundene Parents in ein TDictionary stecken und somit das Auffinden des Parents um einiges beschleunigen.

Zumindest reduziert das die Anzahl der Durchläufe.
  Mit Zitat antworten Zitat
Jumpy

Registriert seit: 9. Dez 2010
Ort: Mönchengladbach
1.733 Beiträge
 
Delphi 6 Enterprise
 
#4

AW: Frage an die Experten für Sortier-Algo-Optimierung

  Alt 30. Sep 2013, 10:28
Welche Datenbank steht denn dahinter? Ich weiß z.B., dass es bei Oracle eine Möglichkeit gibt, die Sortierung in der Datenbank machen zu lassen, wobei ich in alten Kram bei uns gucken müsste wie und womit das gemacht wird.
Ralph
  Mit Zitat antworten Zitat
Benutzerbild von jfheins
jfheins

Registriert seit: 10. Jun 2004
Ort: Garching (TUM)
4.579 Beiträge
 
#5

AW: Frage an die Experten für Sortier-Algo-Optimierung

  Alt 30. Sep 2013, 10:34
Der Ansatz von ManBu wird auch hier gezeigt: http://stackoverflow.com/questions/4...flat-structure

Das wäre immerhin schon lineare Laufzeit (also O(n) worst-case) wenn ich mich nicht irre. (Den erstellten Baum am Ende in-order zu durchlaufen ist ebenfalls linear mit der Anzahl der Elemente) Viel schneller (bzgl. big-O) wird es wohl auch nicht gehen.
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.537 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: Frage an die Experten für Sortier-Algo-Optimierung

  Alt 30. Sep 2013, 10:36
Falls eine Änderung der Tabellenstruktur auch in Frage kommen sollte, sind Nested Sets ggf. einen Blick wert.
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Benutzerbild von Codehunter
Codehunter

Registriert seit: 3. Jun 2003
Ort: Thüringen
2.272 Beiträge
 
Delphi 10.4 Sydney
 
#7

AW: Frage an die Experten für Sortier-Algo-Optimierung

  Alt 30. Sep 2013, 10:48
Ich habe jetzt testweise zum ersten Mal überhaupt mit TDictionary gearbeitet und muss sagen: WHOOOSA!! Das rennt wie die Hölle Den Code jetzt wie folgt umgebaut:
Delphi-Quellcode:
procedure TfrmMain.LoadPlacesTree;
var
  Node, ParentNode: PVirtualNode;
  Place: PPlaceRec;
  Dictionary: TDictionary<Integer, PVirtualNode>;
begin
  with qryPlacesList do begin
    if not Active then Active:= TRUE;
    if RecordCount > 0 then begin
      First;
      tvPlaces.BeginUpdate;
      Dictionary := TDictionary<Integer, PVirtualNode>.Create;
      try
        while not Eof do begin
          Node:= tvPlaces.AddChild(NIL);
          if Assigned(Node) then begin
            Place:= tvPlaces.GetNodeData(Node);
            if Assigned(Place) then begin
              with Place^ do begin
                Id:= Fields.FieldByName('id').AsInteger;
                ParentId:= Fields.FieldByName('parent_id').AsInteger;
                Name:= Fields.FieldByName('name').AsString;
                TypeId:= Fields.FieldByName('type_id').AsInteger;
                AllowedChildTypes:= Fields.FieldByName('allowed_child_types').AsString;

                Dictionary.Add(Id, Node);
              end;
            end;
            Next;
          end;
        end;
      finally
        Node:= tvPlaces.GetFirst;
        while Node <> NIL do begin
          Place:= GetPlaceFromNode(Node);
          if Place <> NIL then begin
            if Dictionary.TryGetValue(Place^.ParentId, ParentNode) then begin
              tvPlaces.MoveTo(Node, ParentNode, amAddChildLast, FALSE);
            end;
          end;
          Node:= tvPlaces.GetNext(Node);
        end;
        Dictionary.Free;
        tvPlaces.FocusedNode:= NIL;
        tvPlaces.EndUpdate;
      end;
    end;
    Active:= FALSE;
  end;
end;
Das Prinzip TDictionary kenne ich ja schon von den assoziativen Arrays bei PHP u.Ä. - dort war ich mit vergleichbaren Algorithmen auch um einiges schneller. Damit ist mir erstmal sehr viel weiter geholfen, VIELEN DANK! Das ist wohl einer der Momente wo man merkt: Man ich war viel zu lange bei Delphi 7 - keine Generics etc. Nützlicher Effekt noch nebenbei: Der Code wurde um einiges kürzer, etliche IFs sind rausgeflogen und lesbarer ist es auch noch ^^

Das gänzlich andere Prinzip "Nested Sets" werde ich mir auf jeden Fall auch anschauen, da ich recht häufig mit derartigen Bäumen zu tun habe. Möglicherweise ergibt sich da ein besseres Anwendungsdesign, mal schauen.
Ich mache grundsätzlich keine Screenshots. Schießen auf Bildschirme gibt nämlich hässliche Pixelfehler und schadet der Gesundheit vom Kollegen gegenüber. I und E zu vertauschen hätte den selben negativen Effekt, würde aber eher dem Betriebsklima schaden
  Mit Zitat antworten Zitat
Blup

Registriert seit: 7. Aug 2008
Ort: Brandenburg
1.429 Beiträge
 
Delphi 10.4 Sydney
 
#8

AW: Frage an die Experten für Sortier-Algo-Optimierung

  Alt 30. Sep 2013, 11:56
Im Prinzip kann man die Aufgabe, die Knoten vorsortiert zu liefern, auch dem Server aufbürden.
Code:
create procedure P_BAUM_SELECT (
    APARENT_ID integer)
returns (
    ID integer,
    ID_PARENT integer,
    NAME varchar(40),
    TYP integer)
as
begin
  if (aparent_id is null) then
  begin
    /* Aufruf vom Client */
    for select id, id_parent, name, typ
    from      t_baum
    where     (id = id_parent)
    into      :id, :id_parent, :name, :typ
    do begin
      suspend;
      for select id, id_parent, name, typ
      from      p_baum_select(:id)
      into     :id, :id_parent, :name, :typ
      do begin
        suspend;
      end
    end
  end
  else
  begin
    /* Rekursion */
    for select id, id_parent, name, typ
    from      t_baum
    where     (id_parent = :aparent_id)
    into      :id, :id_parent, :name, :typ
    do begin
      suspend;
      for select id, id_parent, name, typ
      from      p_baum_select(:id)
      into      :id, :id_parent, :name, :typ
      do begin
        suspend;
      end
    end
  end
end
Wenn die Root-Knoten durch (id_parent is null) gekennzeichnet wären, könte die Abfrage noch etwas schneller sein.
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 20:12 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