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
Seite 1 von 2  1 2      
norwegen60

Registriert seit: 23. Dez 2007
Ort: Schwarzwald
505 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.275 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
955 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
505 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.063 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.
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.

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

Registriert seit: 22. Feb 2017
955 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
505 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
588 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
Hobbycoder

Registriert seit: 22. Feb 2017
955 Beiträge
 
#9

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

  Alt 27. Apr 2017, 09:39
Der Trick einer SQL-Datenbank ist ja, dass man nur das holt, was man auch anzeigen will.
Bei mir passen in das Pulldown einer Compobox wenn sie sehr weit oben oder unter am Bildschirm ist, vielleicht 100 Zeilen.
Du kannst dir ja ein Control schreiben, dass über entsprechende Events verfügt, welche dann neue Datensätze anfordern, wenn der Benutzer an die oberen oder unteren Grenzen der vorhandenen Daten stößt, und diese dann passend einbauen. Bei 100 Datensätzen wären das laut deiner Rechnung ja nur 12ms.
Wie gut und flüssig das dann funktionieren würde weiß ich so nicht, darüber müsste man mal nachdenken. Vielleicht sogar mit einem unteren/oberen Cache. Aber vorstellen könnte ich mir das schon. Allerdings würde ich das wirklich nur dann machen, wenn es keinen anderen Ausweg gibt.

Interessant wäre wirklich mal, warum nicht erst einige Zeichen eingegeben werden können, bevor du die Daten lädst. Bzw. was du damit am Ende erreichen willst.
Gruß Hobbycoder
Alle sagten: "Das geht nicht.". Dann kam einer, der wusste das nicht, und hat's einfach gemacht.

Geändert von Hobbycoder (27. Apr 2017 um 09:41 Uhr)
  Mit Zitat antworten Zitat
norwegen60

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

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

  Alt 28. Apr 2017, 22:00
Ich verstehe nicht warum die Diskussion immer wieder dahin abgleitet ob es sinnvoll ist so viele Daten zu laden. Ich habe nie gesagt, dass irgendjemand durch 100000 oder gar 1.4 Mio Datensätze blättern will.

Als ich das Pulldown vor 30 Jahren eingeführt habe, hat man in SAP noch mühsam über Filter gesucht. Vor ca. 10 Jahren hat Google angefangen nicht nur das anzuzeigen was genau dem Suchbegriff entspricht sondern eine "unscharfe" Suche.

Um dies auch bei langsamem Netz zur Verfügung zu stellen, lade ich eben alle Nr. Das "verschwendet" ca. 135MByte. Zugegeben nicht ganz wenig aber wenn ich seh was Office-Anwendungen so verbraten habe ich kein schlechtes Gewissen und viele der Benutzer sind froh, dass sie auch schnell mal 2 Nummern zurück blättern können. Klar müssen die Daten auch übers Netz, aber nur ein mal. Danach würde ich nur noch die geänderten Daten nachladen und die halten sich sehr in Grenzen. Wenn ich laufend rund ums Tipergebnis lade ist komm ich über dne Tag gesehen wohl auf ähnliche Ergebnisse. Und wenn ich meine Netzload mit der von SAP vergeiche, die in derselben Firma eingesetzt wird, dann brauch ich mir wenig Gedanken machen, ob ich mehr optimieren muss.

Ein Beispiel ist in dem angehängten Bild zu sehen, wo das SQL nicht ganz einfach wäre um auch die Nummern anzuzeigen die vor dem ersten eingetippten Buchstaben H stehen. Und da die Serien-Nr. vielen unterschiedlichen Systematiken unterliegen, weiß ich nicht was direkt vor H kommt. Ausser ich ladem mal sehr großzügig um H herum.

Es geht aber auch gar nicht um 1.4 Mio Datensätze sondern darum wie ich den Inhalt eines Queries schnell und threadsafe an den Maintread übergebe. Und da ist das satzweise übergeben nicht die schnellste Lösung. Wie gut die Lösung letztlich ist, lässt sich eben am einfachsten mit vielen Daten testen. Wenn die schnell gehen dann sind wenig Daten absolut kein Problem.

Zur Bearbeitung lade ich immer nur den Datensatz, den ich tatsächlich brauche.
Miniaturansicht angehängter Grafiken
pulldown.jpg  
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


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 09:50 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