Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   DB-Abfragen in Thread - dann joinen (https://www.delphipraxis.net/202100-db-abfragen-thread-dann-joinen.html)

hschmid67 28. Sep 2019 11:01

DB-Abfragen in Thread - dann joinen
 
Hallo zusammen,

ich habe schon einiges gelesen zu dem Thema, aber so ganz verstanden habe ich es wohl noch nicht. Daher wäre ich froh, wenn mir jemand zu folgendem Problem mit Wissen weiterhelfen könnte:

Ich habe ein paar Tabellen aus unterschiedlichen Datenbanken (mySQL, MSSQL, auch per REST in einem Dataset), deren Daten ich zur Auswertung und Übersicht gerne miteinander verbinden möchte (INNER oder LEFT JOIN). Das ganze geht mit UniDAC VirtualQuery ja ganz gut.

Soweit habe ich das auch schon am Laufen und es ist alles gut - bis auf die Geschwindigkeit.

Nun brauchen die Abfragen aber teilweise länger und in der Zeit steht mein Hauptthread dann und wartet - und der User sieht nichts (außer vielleicht den Warte-Cursor - oder einen entsprechenden Hinweis). Dabei hätte ich die erste Teilabfrage relativ performant und könnte also schon erstmal die Grunddaten liefern, aber solange die anderen zu verknüpfenden Daten nicht da sind, läuft erstmal nichts.

Nun wäre meine Idee, die länger laufenden Abfragen in einen Thread auszulagern und zu Beginn schon mal die ersten Daten, die schneller verfügbar sind, anzuzeigen.

Aber wie bekomme ich das dann aus den Threads wieder zurück, ohne dass ich mir auf Dauer (und vielleicht zunächst nicht sichtbar) mit der VCL Ärger einfange?

Was ich bisher gelesen habe, braucht eine Datenbankabfrage in einem zweiten Thread eine eigene DB-Verbindung.

Konkret nun meine Fragen:

Wenn ich auf dem Mainform die DB-Connections erstelle, diese dann jeweils nur in einem Thread verwende, ist das ok, oder muss ich die DB-Verbindung auch im Thread erstellen, öffnen und wieder schließen? Wenn dem so sein sollte, wie bekomme ich dann die Query mit den Daten (wenn die Verbindung wieder geschlossen ist) aus dem Thread raus?

Kann ich auf dem Mainform (oder einem entsprechenden DataModule) auch die Abfragen platzieren, die ich dann jeweils nur im Thread verwende - hmmm, dann aber doch in die VirtualQuery wieder mit einbinde (dann hätte ich sie ja doch im MailThread auch)?

Oder muss ich die DB-Verbindung im Thread erstellen und auch die Query, dann am Ende des Threads in die VirtualQuery einbinden - und wenn ich den Thread zum Auffrischen dann später mal wieder ausführen sollte, die alte Verbindung und die alte Query erst löschen, dann neu erstellen???

Ihr seht schon, ich stehe auf dem Schlauch - und weiß nicht so recht, was gut wäre und was wirklich funktioniert. Bei meiner Arbeit mit Threads bin ich schon öfter darauf gestoßen, dass es zunächst mal ganz gut funktioniert hat, aber dann doch irgendwann unfassbare Fehler aufgetreten sind, wenn es nicht richtig gemacht war.

Was ich bisher mal versucht habe - und was auch funktioniert, ist folgender Code (zwei TUniConnection, zwei TUniQuery, eine TVirtualQuery - und das Ganze mit OmniThreadLibrary). Die Verbindungen zu den Datenbanken baue ich im FormCreate auf.

Delphi-Quellcode:
procedure TForm16.btn1Click(Sender: TObject);
begin
  vqJoined.DisableControls;
  try
    vqJoined.Close;
    hsDBSetWhereMacro(qPersons, 'WHERE id < 200');
    hsDBSetWhereMacro(qProfil, 'WHERE 1 = 2');
    vqJoined.Open;
  finally
    vqJoined.EnableControls;
  end;
end;

procedure TForm16.btn2Click(Sender: TObject);
begin
  CreateTask(procedure(const mTask: IOmniTask)
    begin
      vqJoined.DisableControls;
      hsDBSetWhereMacro(qProfil, 'WHERE id < 200');
    end)
    .OnTerminated(procedure(const mTask: IOmniTaskControl)
      begin
        vqJoined.Refresh;
        vqJoined.EnableControls;
      end)
    .Unobserved
    .Run;
end;
Was meint ihr? Wie macht ihr sowas, bzw. wo seht ihr die Fallstricke? Und wie könnte ich es sicher machen?

Viele Grüße
Harald

Luckie 28. Sep 2019 11:12

AW: DB-Abfragen in Thread - dann joinen
 
Ich bin da jetzt kein Experte. Aber rein aus dem Gefühl heraus, würde ich die DB Verbindung da erstellen, wo sie gebraucht wird - im Thread.

p80286 28. Sep 2019 14:30

AW: DB-Abfragen in Thread - dann joinen
 
Wenn ich ehrlich bin, habe ich Deine Anforderungen nich so recht verstanden.
Du willst gleichzeitig/hintereinander auf verschiedene Datenbanken zugreifen und die erhaltenen Daten miteinander vergleichen/verknüpfen. Ist das so richig?

Gruß
K-H

hschmid67 28. Sep 2019 16:13

AW: DB-Abfragen in Thread - dann joinen
 
Hallo K-H,

entschuldige, hab mich wohl etwas kompliziert ausgedrückt:

Ich möchte Daten aus verschiedenen Datenbanken abfragen, also 2, 3, 4 Queries und diese dann miteinander verknüpfen, also dann mit einem Join verbinden also etwa

Code:
SELECT
  q1.feld_a,
  q2.feld_b,
  q3.feld_c
FROM
  db1.tabelle1 q1
  LEFT JOIN db2.tabelle2 q2
  LEFT JOIN db3.tabelle3 q3
Und da ich q1 z.B. lokal liegen hab, sind diese Daten sehr schnell verfügbar, q2 kommt aus einem entfernten MSSQL-Server und q3 vielleicht auch noch aus einem REST-Aufruf, dessen Ergebnis ich zum TDataset mache.

Vielleicht noch eine Ergänzung:

Mit TVirtualQuery (ist bei UniDAC von Devart dabei) kann man Abfragen aus verschiedenen Datasets erzeugen. Daher würde das Ganze dann zwar zuletzt in einer Abfrage stattfinden, zunächst aber kommen die db1.tabelle1, db2.tabelle2 und db3.tabelle3 aus jeweils einem eigenen Tquery-Objekt, sind also drei getrennte DB-Aufrufe, die dann in einem TVirtualQuery verbunden werden.

Wie gesagt, das funktioniert ohne Threads sehr gut, nur eben langsam und mit langer Wartezeit...

Viele Grüße
Harald

hschmid67 28. Sep 2019 16:28

AW: DB-Abfragen in Thread - dann joinen
 
Vielleicht kann man meine Frage auf den einfachen Punkt bringen:

Darf man eine DB-Connection und eine Query, die auf einem Form (oder einem Datenmodul) liegen, in einem Thread verwenden und ausführen und dann das Ergebnis mit einer VCL-Komponente auf dem Form verbinden - oder geht das irgendwann schief?

Viele Grüße
Harald

p80286 28. Sep 2019 21:26

AW: DB-Abfragen in Thread - dann joinen
 
Einfache Antwort, ja das geht.
Wichtig ist nur, das der "DB-Thread" vollkommen eigenständig ist und der Zugriff von der Oberfläche auf das Ergebnis erst erfolgt, wenn der Thread beendet ist, bzw. von Seiten des Threads kein Zugriff mehr erfolgt.
(Ich halte wenig von Synchronize!)
Leider kann ich Dir nicht sagen wie das mit Deiner Version zu realisieren ist, ich hab nur Erfahrung mit TThread.

Gruß
K-H

hschmid67 29. Sep 2019 13:55

AW: DB-Abfragen in Thread - dann joinen
 
Hallo K-H,

vielen Dank für die Info - dann werde ich das mal so machen... :-)

Viele Grüße und noch einen sonnigen Sonntag
Harald

Medium 29. Sep 2019 22:35

AW: DB-Abfragen in Thread - dann joinen
 
Zitat:

Zitat von hschmid67 (Beitrag 1448278)
Vielleicht kann man meine Frage auf den einfachen Punkt bringen:

Darf man eine DB-Connection und eine Query, die auf einem Form (oder einem Datenmodul) liegen, in einem Thread verwenden und ausführen und dann das Ergebnis mit einer VCL-Komponente auf dem Form verbinden - oder geht das irgendwann schief?

Zumindest bei MySQL/MariaDB mittels UniDAC bzw. MyDAC bin ich damit schon auf die Nase gefallen. Es geht im Prinzip, und kann auch oft und lange gut gehen, aber wenn es dann auf einmal rummst hat man den Salat. Ich meine irgendwo in der Doku auch mal gelesen zu haben, dass man eine Connection immer nur im selben Thread-Kontext benutzen darf, in dem sie auch erstellt wurde. Seit ich dies mache, habe ich keine Probleme mehr.

Zur Anzeige lohnt es sich dann, eine separate Datenhaltung zu machen, die das Gesamtergenis zugewiesen bekommt. Soll dies auch häppchenweise gehen, müssen alle Zugriffe von in dem Thread (schreibend) und dem Mainthread (lesend) durch Critical Sections abgesichert werden. (Tut man dies nicht, hat man wieder eine ähnliche Situation: Kann lange gut gehen, und dann irgendwann mal nicht und man kratzt sich am Kopf.)

Kleiner Hinweis auf etwas was ich auch erst schmerzlich lernen musste: Wenn du eine Connection im Konstruktor deiner Thread-Klasse erstellst, ist dies noch immer im Kontext des VCL-Threads! Du musst dies in der Execute-Methode machen!


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:37 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