Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi SQL-Abfrage im Thread und füllen eines ListView (https://www.delphipraxis.net/163428-sql-abfrage-im-thread-und-fuellen-eines-listview.html)

Captnemo 28. Sep 2011 15:02

SQL-Abfrage im Thread und füllen eines ListView
 
Hallo Leute,

ich schlage mich jetzt schon einen ganze Weile mit einem mit einem Problem rum, für das ich jetzt nur noch eine Lösung sehe: Threads.

Hier mal zum Problem. Ich habe eine Art Terminplanungsprogramm in dem man Termine für bestimmte Ressourcen verwalten kann. Wenn ich dann einen Termin aufrufe, werden dort die Termindetails dargestellt und eine Historie für die ausgewählte Ressource angezeigt. Alles Funktioniert fehlerfrei, nur die Abfrage der Historie wird bei längerer Laufzeit des Programms ein Zeitfaktor. Der Grund dafür ist klar, denn wenn dann mit der Zeit so 40000 Termine eingetragen sind, und für die Historie eine SQL-Abfrage abgesetzt wird, benötigt der SQL-Server schon allein 5-7 Sekunden um die Daten bereit zu stellen.
Leider wartet das Prog ja nach dem Query1.active:=True eben auf die Rückmeldung des Query's (ich verwende ZEOS und MySQL) bevor es dann mit dem Füllen des ListView weitergeht. Letzterer Schritt ist dann wieder uneingeschränkt schnell.

Mein einziger erfolgversprechende Lösungsansatz wäre es, die SQL-Abfrage in einen Thread auszulagern, von diesem dann nach dem Anzeigen der Terminform unabhängig von Usereingaben das Listview füllen zu lassen.
So fällt für den User die Wartezeit nicht mehr so deutlich auf, und im Listview könnte man solange ein "Daten werden abgerufen..." darstellen.

So weit so gut. Aber wie fange ich das an. Leider hab ich von Threads recht wenig Ahnung und alle Tutorials und Bespiele beschränken sich leider auf recht simple Dinge wie eine String übergeben oder ein paar Werte zu berechnen.

Meine erste Frage in diesem Zusammenhang, muß ich für einen Thread eine extra Unit verwenden, oder kann ich das in meiner Termin Unit mit unterbringen?

Die zweite Frage wäre, kann ich aus einem Thread einfach per uses mein Datamodul einbinden und auf die Querys und die Connection zugreifen?

Kann mein Thread dann einfach so auf das Listview zugreifen?

Wie gesagt, ich hab von Thread wenig Ahnung, und ein Tutorial was mir diese Fragen beantwortet hab ich leider noch nicht gefunden.

Bernhard Geyer 28. Sep 2011 15:09

AW: SQL-Abfrage im Thread und füllen eines ListView
 
Zitat:

Zitat von Captnemo (Beitrag 1127277)
Der Grund dafür ist klar, denn wenn dann mit der Zeit so 40000 Termine eingetragen sind, und für die Historie eine SQL-Abfrage abgesetzt wird, benötigt der SQL-Server schon allein 5-7 Sekunden um die Daten bereit zu stellen.

Ist die Datenmenge so groß das es so lange dauert? Sollte normalerweis in << 1s möglich sein wenn Bandbreite genügend da ist bzw. Server korrekt konfiguiert. Evtl. nicht alle Detaildaten laden die man eh nicht sofort sieht.

Zitat:

Zitat von Captnemo (Beitrag 1127277)
Mein einziger erfolgversprechende Lösungsansatz wäre es, die SQL-Abfrage in einen Thread auszulagern, ...

Sollte kein Problem sein. Manche DB-Zugriffskompos erfordern das du dann auch im Thread eine eigene Connection hast.

Zitat:

Zitat von Captnemo (Beitrag 1127277)
... von diesem dann nach dem Anzeigen der Terminform unabhängig von Usereingaben das Listview füllen zu lassen

Die Listview wirst du nicht über den Thread füllen können da die VCL bzw. die zugrunde liegenden Win-Controls eine Thread-Affinität haben. Du darfst sie nur im erzeugenden Thread verwenden.

Sinnvoller ist hier die Daten en block/blockweise zurück an die Hauptapp zu geben und dann die Listview im Virtual Modus zu betreiben.

Union 28. Sep 2011 15:15

AW: SQL-Abfrage im Thread und füllen eines ListView
 
Genau, hol doch nur die 10..100 neuesten History-Einträge pro Ressource und mach wenn der Benutzer weiter nach unten blättert ein Refresh auf alle. Oder einen Button "Alle Anzeigen"

webcss 28. Sep 2011 15:16

AW: SQL-Abfrage im Thread und füllen eines ListView
 
Für eine genaue Antwort müsste ich ein wenig in meiner "Krabbelkiste" kramen, aber aus dem Kopf soviel:

Du erstellst deinen nebenläufigen Thread. Dieser *muss* eine eigene Verbindung aufbauen (Database, Transaction + Query!), er darf nicht die Verbindung des MainThread benutzen sonst knallt's.

Den startest Du mit deiner Abfrage und lässt in nebenher dudeln. Wenn das Abfrageergebnis komplett ist, terminiert sich der Nebenthread idealerweise selbst.

Um die Performance zu steigern, solltest du die ListView im VirtualMode (siehe OnData der ListView) einsetzen.

Gruß
Clemens

Captnemo 28. Sep 2011 16:13

AW: SQL-Abfrage im Thread und füllen eines ListView
 
Zitat:

Zitat von Bernhard Geyer (Beitrag 1127278)
Ist die Datenmenge so groß das es so lange dauert? Sollte normalerweis in << 1s möglich sein wenn Bandbreite genügend da ist bzw. Server korrekt konfiguiert. Evtl. nicht alle Detaildaten laden die man eh nicht sofort sieht

Naja, ich hab festgestellt, dass die Abfrage bei 40000 DS schon so an die 4,6 Sekunden rankommt. Das sind die gesammelten Termine der letzten 3 Jahre. In weiteren 3 Jahren sind es schon 80000 DS und ich denke nicht, dass sich der Zeitbedarf linear zu DS-Anzahl verhält. aber das könnte man ja ausprobieren.

Ich habe auch erst gedacht es länge an meiner Anwendung, aber als ich dann direkt mit dem MySQL-Adminstrator die gleichen Zeiten gemessen habe, denke ich mir das es wohl weniger an meiner Anwendung zu tun hat.

So, ich werd das mal probieren mit ein thread.

Aber meine erste Frage ist noch offen. Kann ich in der Terminunit einfach mit
Code:
Type
   MyThread = TThread
   end;
den Thread mit einfügen, oder muß dafür eine eigene Unit existieren?

wie ich die Daten an die Hauptapp zurückgeben weiß ich auch noch nicht, aber einen Schritt nach dem anderen.

jfheins 28. Sep 2011 17:27

AW: SQL-Abfrage im Thread und füllen eines ListView
 
Zitat:

Zitat von Captnemo (Beitrag 1127296)
Aber meine erste Frage ist noch offen. Kann ich in der Terminunit einfach mit
Code:
Type
   MyThread = TThread
   end;
den Thread mit einfügen, oder muß dafür eine eigene Unit existieren?

Ja, das sollte ohne Probleme gehen. Die eigentliche Arbeit übernimmt ja die Klasse.
Zitat:

wie ich die Daten an die Hauptapp zurückgeben weiß ich auch noch nicht, aber einen Schritt nach dem anderen.
Z.B. ein Array of record erstellen (der record enthält schon genau die felder, die später in die Spalten der Listview kommen) und dann das zurückgeben.

Mal ne andere Frage: Was genau dauert lange? Wenn du von den 40.000 Datensätzen einen auswählst, das abrufen der History oder das übertragen der Daten? Ich bin mir ziemlich sicher dass das schneller gehen muss. Vll. keine Indizies gesetzt? Falsche indizes?

Captnemo 28. Sep 2011 17:49

AW: SQL-Abfrage im Thread und füllen eines ListView
 
Tja, was das explizit so lange dauert kann ich dir nicht genau sagen.
Ich habe versucht das ganz mit Indizies versucht, aber ich habe dadurch keine nennenswerten Verbesserungen festgestellt. Ich mir sogar ein View erzeugt, welches nur die Felder enthält (sind glaub ich 6), aber auch wenn ich das abfrage, bekommen ich keine Geschwindigkeitssteigerung.

Die Tabelle dir ich abfrage hat so 49 Felder, davon sind 2 Blob's der Rest String, Int und Datetime. Die beiden Blob's verwende ich einmal für die Ablage von Richtext und das andere für eine kleine Grafik 20x50 Pixel, welche aber nur bei den wenigsten DS tatsächlich gefüllt ist.

In der View sind aber nur Datetime, String und int.

Ich habe auch schon die Speichergrenzen in den Variablen des MySQL-Server angehoben, auch keine nennenswerten veränderungen. Wenn ich einen einzelnen DS über Lfdnr (primärindex) abfrage, geht das sehr schnell.

Ich hab auch schon mal hier im Forum gefragt, wie MySQL den zu verwendeten Index auswähl. Da hieß es, wenn er existiert wird er automatisch verwendet. Ich weiß aber nicht, ob das stimmt.

Beim Thread habe ich so meine Probleme. Da ich im Thread ein eigene Connection- und Query-Object verwenden soll, muß ich diese ja auch erst einmal erzeugen. Das kann aich aber nicht in der procedure Execute machen, sonder eher in einer Create. Gibt's die auch in einem Thread? Wenn ich in D7 über Datei-Neu-Weitere eine TThread-Unit anlege, dann gibt es dort keine Create-procedure. Und wenn ich eine anlege, dann meckert der Compiler.

Grundsätzlich ist mir jede Lösung recht. Ohne TThread wär's an dieser Stelle einfacher. Andererseits würd ich gerne auch mal endlich diesen TThread-Kram verstehen. Steh da irgendwie auf einem Schlauch. :wall::wall:

Sir Rufo 28. Sep 2011 20:41

AW: SQL-Abfrage im Thread und füllen eines ListView
 
Jede (wirklich jede) Klasse hat eine Constructor und der heißt auch eigentlich Create.

Wenn der Compiler meckert, dann, weil die Klasse abgeleitet wurde (die ist immer abgeleitet, min. von TObject) und im Vorfahr etwas anders deklariert war, bzw. du das mit dem inherited oder inherited Create so nicht passt.

Hilfreich dazu ist es sich die Vorfahr-Klasse mal anzuschauen, dann sieht man auch, wie der Constructor da aussieht ;)

Ansonsten kann ich nur mal den Tip geben, im interface-Teil der neu zu erstellenden Klasse mal Strg-Leertaste zu drücken und dann wundern was da so auftaucht :mrgreen:

FredlFesl 28. Sep 2011 21:24

AW: SQL-Abfrage im Thread und füllen eines ListView
 
Wieso musst Du eigentlich alle 40.000 Einträge anzeigen?
Wie ich das machen würde?
Fetch-on-Demand. Ich lade mir 1000 oder so Einträge, und wenn jemand nach unten scrollt und den 1001sten sehen will, lade ich den nächsten Happen ein. Die Happen (1000 oder so) sind so gewählt, das das Laden sehr schnell geht.

Die Query sieht immer gleich aus:
Code:
select first 1000 * from MyView where SortColumn>:LastColumn
Und der Parameter :LastColumn enthält den Wert der Spalte 'SortColumn' des jeweils untersten Eintrags der breits geladenen Daten.

Eigentlich keine große Sache. Kann aber sein, das dann die Query selbst lahm wird (wenn man die DB nicht richtig gepimpt hat).

Medium 28. Sep 2011 22:41

AW: SQL-Abfrage im Thread und füllen eines ListView
 
Wir haben das SQL Statement zwar nie gesehen bisher (das, und die Tabellendefinition wären nicht ganz unnützlich beim Analysieren hier ;) ), aber ich schmeisse zudem auch einfach mal in den Raum, dass Volltextsuchen der Art "LIKE '%substr%'" jeden Index aushebeln, und öfter mal Ursache für Performanceprobleme sind. Zeig doch mal das SELECT, sowie das aus der Tabelle erzeugte CREATE Statement her. 40k Sätze sind eigentlich nicht wirklich eine große Sache, ausser dein Server geht über eine eher mäßig schnelle Verbindung. Danach wurde übrigens auch schon gefragt: Ist der Server lokal, oder eine andere Maschine? Wenn letzteres: Wie ist er an deinen Client angebunden?


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:24 Uhr.
Seite 1 von 3  1 23      

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