AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Datenbanken Große Datenmenge in LookupCombo zur Verfügung stellen
Thema durchsuchen
Ansicht
Themen-Optionen

Große Datenmenge in LookupCombo zur Verfügung stellen

Ein Thema von norwegen60 · begonnen am 23. Apr 2017 · letzter Beitrag vom 29. Apr 2017
Antwort Antwort
norwegen60

Registriert seit: 23. Dez 2007
Ort: Schwarzwald
509 Beiträge
 
Delphi 12 Athens
 
#1

Große Datenmenge in LookupCombo zur Verfügung stellen

  Alt 23. Apr 2017, 13:30
Datenbank: mssql • Version: 2008 • Zugriff über: unidac
Hallo,

ich stelle den Benutzern eine DBLookupComboBox zur Verfügung in der 1.4 Mio Datensätze zur Auswahl stehen. In der hinterlegten Query werden nur 3 der 30 Felder der DB-Tabelle geführt (ID, Serien-Nr., Typ).

Warum alle Daten:
Das Kunden-Netz ist teilweise sehr langsam so dass Echtzeitfilter zu Verzögerungen führen
Manchmal will der Nutzer ab der aktuellen Serien-Nr noch ein paar Datensätze vor oder zurück blättern.

Sobald die Daten geladen sind, funktioniert das Ganze auch einwandfrei und sehr schnell.

Aufgabe:
Wie kann ich die Zwangspause beim Laden der Daten beim Start verhindern
Wie kann ich die Daten hin und wieder im Hintergrund ohne für den User bemerkbare Aussetzer aktualisieren?

Lösungsansatz:
Laden und Aktualsierung erfolgt in einem Thread

Meine Frage:
Zuerst dachte ich es sei eine gute Idee die Daten in einer UniQuery in einem Thread abzurufen und dann an eine VirtualQuery zu übergeben. Beim Test habe ich dann aber festgestellt, dass das UniQuery.Open auf meinem Rechner 1s dauert und das VirtualQuery.Open dann 7s. In beiden Fällen ist FetchAll = True. Im Kundennetzt dauert das Ganze um einiges länger.

Wie müsste Execute/Synchronice gestaltet werden, dass die Daten ohne spürbare Verzögerung an den Main-Thread übergebe werden und der User u.U. im Mainthread in den Daten gerade blättert.

Ich habe dazu schon einige Foren durchsucht aber nie eine funktionierenden Lösungsansatz gefunden.

Wenn einer eine gute Idee hat, wäre ich sehr dankbar.

Gerd
  Mit Zitat antworten Zitat
hoika

Registriert seit: 5. Jul 2006
Ort: Magdeburg
8.276 Beiträge
 
Delphi 10.4 Sydney
 
#2

AW: Große Datenmenge in LookupCombo zur Verfügung stellen

  Alt 24. Apr 2017, 08:28
Hallo,
Zitat:
ich stelle den Benutzern eine DBLookupComboBox zur Verfügung in der 1.4 Mio Datensätze zur Auswahl stehen. In der hinterlegten Query werden nur 3 der 30 Felder der DB-Tabelle geführt (ID, Serien-Nr., Typ).
Ob die 3 oder 4 Felder anzeigst, die 1.4 Mio Datensätze sind das Problem.
Die MB's an Daten müssen ja erst mal übers Netz zu Dir kommen.

Ich würde ein eigenes Fenster zur Auswahl bauen und dort muss mindestens ein Filterkriterium eingegeben werden.
Heiko
  Mit Zitat antworten Zitat
Hobbycoder

Registriert seit: 22. Feb 2017
998 Beiträge
 
#3

AW: Große Datenmenge in LookupCombo zur Verfügung stellen

  Alt 24. Apr 2017, 08:51
Bei 1,4 Mio. Datensätzen macht es für den Benutzer überhaupt keinen Sinn, dass diese bereits vor dem ersten Zeichen zur Verfügung stehen. Je nach Aufbaue der Lookup-Daten wartest du also mal mindestens eine gewisse Anzahl von Zeichen ab, bevor du überhaupt etwas als Lookup zur Verfügung stellst.
Kein User dieser Welt würde durch 1,4 Mio. Lookupwerte blättern.

Wenn die Seriennummer z.B. folgendermaßen Aufgebaut ist:
1-123-45678
1-123-45679
1-123-45681
1-124-56789

könntest du nach Eingabe der 1 den Bindestrich automatisch setzen, und die 123 und 124 als Lookup laden. Nach Eingabe oder Auswahl von 123 setzt du wieder den Bindestrich automatisch und lädst dir 45678, 45679 und 45681 ins Lookup (kann man ja ober Group by ganz gut lösen). Das ganze vielleicht über eine Thread, so dass möglichst wenig Beeinträchtigung in der Gui entsteht.
Wenn die Seriennummern nur aus Zahlen besteht, dann könntest du das natürlich ähnlich machen.


Eine andere Möglichkeit wäre, wenn das Datenbankdesign in deinen Händen liegt, jede Seriennummer mit einer Versionskennung zu versehen. Das könnte ein Int oder ein Timestamp sein, aber auch ein Autoinc-feld. Beim Start des Programms lädst du jetzt alle 1,4 Mio. Datensätze per Thread bereits im Hintergrund ein, merkst dir aber den höchsten Int, Timestamp oder Autoinc-Wert. Kurz vor der dem Bestücken des Lookups schaust du noch mal kurz in die DB und lädst alle Datensätze nach, die eine höhere Versionskennung haben, also seit dem STart des Programms in der Tabelle hinzu gekommen sind. Das dürfte wesentlich weniger sein. Die Pflegst du dann in deine bereits geladenen ein.
Diese Möglichkeit ist natürlich bei 1,4 Mio. Datensätze sehr Speicherlastig.
So mache ich das in einem Programm, in dem alle Kundennummern zur Verfügung stehen sollen. Sind aber keine 1,4 Mio. und ich mache das, weil ich die auch bei Unterbrechung der DB-Anbindung zur Verfügung haben will.

Gruß Hobbycoder

Geändert von Hobbycoder (24. Apr 2017 um 08:53 Uhr)
  Mit Zitat antworten Zitat
norwegen60

Registriert seit: 23. Dez 2007
Ort: Schwarzwald
509 Beiträge
 
Delphi 12 Athens
 
#4

AW: Große Datenmenge in LookupCombo zur Verfügung stellen

  Alt 24. Apr 2017, 21:39
Kein User dieser Welt würde durch 1,4 Mio. Lookupwerte blättern.
Das habe ich auch nie behauptet. Wenn er aber wissen will welche Serien-Nr. vor "F%" kommt, gibt er F ein und blätert 1 zurück, bzw. sieht es schon in der Loopup.

Wenn die Seriennummer z.B. folgendermaßen Aufgebaut ist:
1-123-45678
1-123-45679
Es werden auch die Serien-Nr. von Zukaufteilen verwaltet so dass es keine Systematik gibt.

Kurz vor dem Bestücken des Lookups schaust du noch mal kurz in die DB und lädst alle Datensätze nach, die eine höhere Versionskennung haben, also seit dem STart des Programms in der Tabelle hinzu gekommen sind. Das dürfte wesentlich weniger sein. Die Pflegst du dann in deine bereits geladenen ein.
Genau da will ich hin. Das SQL um geänderte Sätze zu finden sollte nicht das Problem sein. Wie aber kapsele ich das in einen Thread der auch das Anfügen an die schon eingelesenen Daten übernimmt ohne die GUI einzufrieren[/QUOTE]
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.314 Beiträge
 
Delphi 12 Athens
 
#5

AW: Große Datenmenge in LookupCombo zur Verfügung stellen

  Alt 24. Apr 2017, 22:25
Häng noch ein LIMIT 1000 an und schon wird es auch nie zuviel.
Dann kannst du die Daten auch problemlos im Haupthtread laden, ohne dass man zu lange warten muß.

z.B. erst ab 3 Zeichen automatisch laden (mit Enter sofort)
Und das Laden nicht sofort starten, sondern einen Timer 750ms bis 2500ms beim OnChange/KeyPress starten/restarten und bei Ablauf dann erst laden.
So kann der User auch mal schneller was eintippen und wird nicht sofort ausgebremst, bis er selber ne "längere" Pause macht.



Kommt aber auch darauf an, wie schnell die Daten geladen werden.
Man könnte auch alle Daten laden und dann nur noch über einen ClientFilter begrenzen.
Oftmals ist es ja so, dass die Daten mindestens genauso schnell da sind, wie für die aktualisierung der GUI brauch, inkl. einer Synchronisierung, wenn das Datenladen im Thread ablief.
Ein Therapeut entspricht 1024 Gigapeut.

Geändert von himitsu (24. Apr 2017 um 22:31 Uhr)
  Mit Zitat antworten Zitat
Hobbycoder

Registriert seit: 22. Feb 2017
998 Beiträge
 
#6

AW: Große Datenmenge in LookupCombo zur Verfügung stellen

  Alt 24. Apr 2017, 22:39
Also beim Programmstart würdest du die 1,4 Mio. Datensätze erst mal einlesen. Das würde natürlich auch zu einer Beeinträchtigung der GUI führen, deswegen wäre es auch dort sinnvoll das ganze über einen Thread zu lösen.

Hier mal ein Threadgerüst (ist nur so zusammengekloppt)

Delphi-Quellcode:
unit LoadLookupDataThread;

interface

uses classes;

Type
  TOnNewSernr=procedure(sender: TObject; SerNr: string) of object;
  TOnThreadFinished=procedure(sender: TObject; MaxID: integer) of object;

  TLoadLookupData=class(TThread)
  private
    FDBHost, FDBUser, FDBPass: string;
    FLastVersionnr: integer;
    FOnNewSernr: TOnNewSernr;
    FOnTheadFinished: TOnThreadFinished;
    procedure DoOnNewSernr(Sernr: string);
    procedure DoOnThreadFinished(MaxID: integer);
  public
    constructor Create(suspended: boolean; DBHost, DBUser, DBPass: string;
      LastVersionNr: Integer);
  protected
    procedure Execute; override;
  published
    property OnNewSerNr: TOnNewSernr read FOnNewSernr write FOnNewSernr;
    property OnThreadFinished: TOnThreadFinished read FOnTheadFinished write FOnTheadFinished;
  end;

implementation

{ TLoadLookupData }

constructor TLoadLookupData.Create(suspended: boolean; DBHost, DBUser,
  DBPass: string; LastVersionNr: Integer);
begin
  inherited Create(suspended);
  Self.FreeOnTerminate:=True;
  Self.NameThreadForDebugging('LoadLookupDataThread');
  FDBHost:=DBHost;
  FDBUser:=DBUser;
  FDBPass:=DBpass;
  FLastVersionnr:=LastVersionNr;
end;

procedure TLoadLookupData.DoOnNewSernr(Sernr: string);
begin
  if Assigned(FOnNewSernr) then
    Synchronize(procedure
      begin
        FOnNewSernr(self, Sernr);
      end);
end;

procedure TLoadLookupData.DoOnThreadFinished(MaxID: integer);
begin
  if Assigned(FOnTheadFinished) then
    Synchronize(procedure
      begin
        FOnTheadFinished(self, MaxID);
      end);
end;

procedure TLoadLookupData.Execute;
var
  FDBConnection: TDBConnection;
  FDBQuery: TDBQuery;
  MaxID: integer;
begin
  MaxID: FLastVersionnr;
  FDBConnection:=TDBConnection.Create(nil);
  FDBQuery:=TDBQuery.Create(nil);
  try
    FDBConnection.Host:=FDBHost;
    FDBConnection.User:=FDBUser;
    FDBConnection.Password:=FDBPass;
    FDBConnection.connect;
    if FDBConnection.connected then
    begin
      FDBQuery.Connection:=FDBConnection;
      FDBQuery.SQL.Text:='Select Sernr, Id from Seriennummern where ID>:id';
      FDBQuery.Params.parseSQL(FDBQuery.SQL.Text, True);
      FDBQuery.Params.paramValue['id']:=FLastVersionnr;
      FDBQuery.Active:=True;
      while not FDBQuery.Eof do
      begin
        DoOnNewSernr(FDBQuery.Fieldbyname('Sernr').AsString);
        if FDBQuery.Fieldbyname('id').asInteger>MaxID then
          MaxID:=FDBQuery.Fieldbyname('id').AsInteger;
        FDBQuery.Next;
      end;
      FDBQuery.Active:=False;
    end;
  finally
    FDBConnection.Free;
    FDBQuery.Free;
    DoOnThreadFinished(MaxID);
  end;
end;

end.
aufruf:

Delphi-Quellcode:
var
  LastID: Ingeger=-1; //irgendwo definieren

procedure TForm1.LadeLookupDaten;
var
  tld: TLoadLookupData;
begin
  ComboBox1.Items.clear;
  tld:=TLoadLookupData.create(false, Hostname, Username, Password, MaxID);
  tld.OnNewSernr:=NewSernr;
  tld.OnThreadFinished:=Einlesenfertig
  tld.Resume;
end;

procedure TForm1.NewSernr(sender: TObject; Sernr: string);
begin
  ComboBox1.Items.Add(Sernr);
end;

procedure TForm1.Einlesenfertig(Sender: TObject; MaxID: integer);
begin
  LastID:=MaxID
  ShowMessage('Alle Lookup-Daten eingelesen';
end;
Bevor nun die Form angezeigt wird, wo dann das Lookup benötigt wird, rufst du den Thead erneut auf, übergibts dann aber mit LastID eben die letzte zurückgelieferte LastID.
Ist natürlich nur ein Beispiel, dass du jetzt nach deinen Anforderungen anpassen musst.
Und die Lookupdaten solltest du natürlich sortieren, sonst nützt das blättern natürlich auch nichts, dass ansonsten ja alle neuen Sernr hinten angefügt würden.
Gruß Hobbycoder
Alle sagten: "Das geht nicht.". Dann kam einer, der wusste das nicht, und hat's einfach gemacht.

Geändert von Hobbycoder (24. Apr 2017 um 22:44 Uhr)
  Mit Zitat antworten Zitat
norwegen60

Registriert seit: 23. Dez 2007
Ort: Schwarzwald
509 Beiträge
 
Delphi 12 Athens
 
#7

AW: Große Datenmenge in LookupCombo zur Verfügung stellen

  Alt 26. Apr 2017, 23:52
Hallo Hobbycoder,

ja, genau. So was in der Art suche ich und hatte ich auch schon dran rum probiert. Ich hatte allerdings gehofft eine effizientere Methode zu bekommen, wie ich die Daten übertragen kann. Mit Next durch die Daten zu blättern und jeden Wert einzeln zu syncen dauert ziemlich lang. Bei mir ca 12s für 100000 Sätze. Ich selber hatte es mit einer typisierten Listen gemacht. Dauert aber immer noch 7s / 100000.

Kann man die Daten aus dem Query nicht noch effektiver übergeben? Und dann später auch neu Datensätze anhängen? Wobei hier das einzeln übertragen nicht mehr ganz so tragisch wäre.

Grüße
Gerd
  Mit Zitat antworten Zitat
sko1

Registriert seit: 27. Jan 2017
601 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#8

AW: Große Datenmenge in LookupCombo zur Verfügung stellen

  Alt 27. Apr 2017, 07:27
Zitat:
Bei mir ca 12s für 100000 Sätze.
Wer will denn durch 100000 Datensätze blättern, ich halte den Ansatz schon für fragwürdig!
Gibt es einen eindeutigen Index?
Wenn ja dann zeige einen Datensatz an, soll zum nächsten gesprungen werden dann starte eine neue Abfrage die Dir einen Datensatz liefert dessen Index > dem gerade angezeigten ist, Rückwärts analog...
Jede dieser neuen Anfragen wird wenige Millisekunden brauchen von denen der Bediener nichts mitbekommt.

Ciao
Stefan
  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 04:52 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz