AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Sonstige Fragen zu Delphi Delphi SQL-Abfrage im Thread und füllen eines ListView
Thema durchsuchen
Ansicht
Themen-Optionen

SQL-Abfrage im Thread und füllen eines ListView

Ein Thema von Captnemo · begonnen am 28. Sep 2011 · letzter Beitrag vom 29. Sep 2011
Antwort Antwort
Seite 2 von 3     12 3      
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#11

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

  Alt 29. Sep 2011, 00:11
Ok, hier mal ein Thread, der Datensätze an eine TListView schickt.

Der passt da noch nicht ganz für Dich (kein ZEOS, keine Query) aber das Prinzip bleibt gleich.

Daten lesen und dann immer blockweise an die ListView schicken.
so lange wie der Thread noch läuft per Queue und der letzte Abgleich erfolgt per Synchronize.
(Damit wird sichergestellt, dass alle Einträge auch die ListView ausgeliefert werden)

Delphi-Quellcode:
unit thread.SqlDataToListView;

interface

uses
  Classes,
  ComCtrls, // TListView
  DB, DBClient, // TClientDataSet
  Generics.Collections; // TList<T>

type
  TSqlDataToListViewThread = class( TThread )
  private
    FListView : TListView;
    FData : TDataSet;
  protected
    procedure SendDataToListView( AListView : TListView; AItems : TList<TStrings>; Sync : Boolean = False );
  protected
    procedure Execute; override;

  public
    constructor Create( AListView : TListView; const AFileName : string; CreateSuspended : Boolean = False );
    destructor Destroy; override;
  end;

implementation

uses
  Windows, SysUtils;

{ TGetSqlDataToListViewThread }

constructor TSqlDataToListViewThread.Create( AListView : TListView; const AFileName : string; CreateSuspended : Boolean );
begin
  inherited Create( CreateSuspended );

  FListView := AListView;
  FData := TClientDataSet.Create( nil );

  with FData as TClientDataSet do
    begin
      FileName := AFileName;
    end;
end;

destructor TSqlDataToListViewThread.Destroy;
begin
  FData.Free;
  inherited;
end;

procedure TSqlDataToListViewThread.Execute;
var
  lItems : TList<TStrings>;
  lItem : TStrings;
  lField : TField;

begin

  // Datenverbindung öffnen

  try
    FData.Open;
  except
    on E : Exception do
  end;

  if FData.Active
  then

    // Wenn die Datenverbindung gesichert hergestellt ist dann können wir ja ans Werk

    try

      lItems := TList<TStrings>.Create;
      try

        while not Terminated and not FData.Eof do

          // Wir machen hier so lange, bis ...
          // ... der Thread abgebrochen wird
          // ... oder alle Datensätze gelesen wurden

          begin

            // Daten in einen Puffer schieben

            lItem := TStringList.Create;
            for lField in FData.Fields do
              begin
                lItem.Add( lField.AsString );
              end;

            lItem.Add( DateTimeToStr( now ) );

            // Daten in die Sammelliste schreiben

            lItems.Add( lItem );

            // Nächster Datensatz
            FData.Next;

            // Wir tun mal so, als ob das hier gaaaanz lange dauert
            Sleep( Random( 15 ) );


            if ( lItems.Count >= 10 ) or Terminated or FData.Eof
            then

              // Wenn der Block voll ist,
              // oder der Thread abgebrochen wurde
              // oder keine Daten mehr zu lesen sind
              // dann die Daten an das ListView ausliefern

              SendDataToListView( FListView, lItems, Terminated or FData.Eof );

          end;

      finally
        lItems.Free;
      end;

    finally
      FData.Close;
    end;
end;

procedure TSqlDataToListViewThread.SendDataToListView( AListView : TListView; AItems : TList<TStrings>; Sync : Boolean );
var
  lItems : TObjectList<TStrings>; // Mal hier schnell geändert, sonst haben wir da ein Speicherleck :o)
  lItem : TStrings;
begin
  if MainThreadID = GetCurrentThreadId
  then

    // Ei jo, wenn wir uns jetzt im MainThread-Kontext befinden,
    // dann können wir ja wieder ganz gemütlich auf das VCL-Gedöns zugreifen

    begin

      if Assigned( AItems )
      then
        begin

          AListView.Items.BeginUpdate;
          try

            for lItem in AItems do
              begin
                with AListView.Items.Add do
                  begin
                    Caption := lItem[0];
                    lItem.Delete( 0 );
                    SubItems.Assign( lItem );
                    SubItems.Add( DateTimeToStr( now ) );
                    SubItems.Add( BoolToStr( Sync, True ) );
                  end;
              end;

          finally
            AListView.Items.EndUpdate;
          end;

          AItems.Free; // ** Hier ist das Free, und ...
        end;

    end
  else
    begin

      // Kopieren der übergebenen Daten-Liste
      lItems := TObjectList<TStrings>.Create; // ** ... hier das Create ... verkehrte Welt :o)
      for lItem in AItems do
        begin
          lItems.Add( lItem );
        end;

      // übergebene Daten-Liste leeren (da schreibt der Thread ja wieder neue Daten rein)
      AItems.Clear;

      // Jetzt rufen wir uns selber nochmal auf, aber ...
      // 1. mit der kopierten Liste
      // 2. im MainThread-Kontext (Synchronized oder Gequeued)

      if Sync
      then
        Synchronize( procedure begin SendDataToListView( AListView, lItems, Sync ); end )
      else
        Queue( procedure begin SendDataToListView( AListView, lItems, Sync ); end );
    end;
end;

end.
Im Anhang ScreenShot, Exe-Datei und der gesamte Source
Miniaturansicht angehängter Grafiken
threadgetsqldata_s1.png  
Angehängte Dateien
Dateityp: zip ThreadGetSQLData.zip (834,1 KB, 37x aufgerufen)
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)

Geändert von Sir Rufo (29. Sep 2011 um 00:25 Uhr)
  Mit Zitat antworten Zitat
FredlFesl

Registriert seit: 19. Apr 2011
293 Beiträge
 
Delphi 2009 Enterprise
 
#12

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

  Alt 29. Sep 2011, 07:52
Hmm.. Bisserl kompliziert (zu viel Rumkopiererei), wenn man bedenkt, das man die Daten beim virtual Mode gar nicht in die ListView schreiben muss. Ich würde die Daten im Hintergrund einfach in eine TObjectList<TStringList> füllen und immer dann, wenn ein weiterer Block befüllt wurde, einfach per Synchronize die ListView.Items.Count neu setzen.

Aber als allgemeingültiges Proof-Of-Concept natürlich ok.
Das Bild hängt schief.
  Mit Zitat antworten Zitat
Benutzerbild von Captnemo
Captnemo

Registriert seit: 27. Jan 2003
Ort: Bodenwerder
1.126 Beiträge
 
Delphi XE4 Architect
 
#13

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

  Alt 29. Sep 2011, 07:57
Also als erstes heut morgen möchte ich gleich mal eine Kniefall für Sir Rufo machen. Das ich im Create gleich das Zeilobject mit übergeben kann, da wär ich im Leben nicht drauf gekommen. Wenn ich das so richtig verstanden habe, dann kann in der Create-Procedure dann ein Connection und Query object erzeugen. Das SQL-Statement könnte ich anstelle von afilename übergeben.
Und das Execute wird nur einmal ausgeführt.

Damit werd ich das jetzt mal versuchen. Was ich bisher aus den verschieden Beispielen zusammengebastelt hatte hatte schon eine gewisse ähnlichkeit mit dem was du hier gepostet hast. Nur hab ich versucht, die Paramter für die Abfrage per Property zu übergeben, aber das hat noch nicht funktioniert. Wenn ich das gleich im Create machen kann spar ich mir noch code.

Okay, ich frage mich grad noch, wann und wie sich der Thread dann zerstört? Und ich müßte die Objekte, die ich im Thread erzeuge ja auch noch freigeben. Dafür würde ich den destructor Destroy nehmen, wenn das geht.


Zur Tabelle hier das das Create
Code:
CREATE TABLE `main` (
  `Lfdnr` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `Datum` datetime NOT NULL,
  `Zeit` varchar(45) DEFAULT NULL,
  `Da` varchar(45) DEFAULT '0',
  `Erl` varchar(45) DEFAULT '0',
  `Name` varchar(45) DEFAULT NULL,
  `Wer` varchar(45) DEFAULT NULL,
  `Termin` varchar(45) DEFAULT NULL,
  `Typ` varchar(45) DEFAULT NULL,
  `KZ` varchar(45) DEFAULT NULL,
  `Arbeiten` varchar(250) DEFAULT NULL,
  `Telefon` varchar(45) DEFAULT NULL,
  `Bemerkung` longtext,
  `Farbe` varchar(45) DEFAULT 'clScrollBar',
  `ZT` varchar(45) DEFAULT '0',
  `TB` varchar(45) DEFAULT '0',
  `TD` varchar(45) DEFAULT '0',
  `MA` varchar(45) DEFAULT NULL,
  `EW` varchar(45) DEFAULT '0',
  `Erinnerung` varchar(45) DEFAULT NULL,
  `FormatArbeiten` blob,
  `ID` varchar(45) DEFAULT NULL,
  `Mondruck` int(10) unsigned DEFAULT '0',
  `Auftrnr` varchar(10) DEFAULT NULL,
  `InArbeit` varchar(45) DEFAULT NULL,
  `rg` varchar(45) DEFAULT '0',
  `kmstand` int(10) unsigned DEFAULT '0',
  `recall` int(11) DEFAULT '-1',
  `lkukon` datetime DEFAULT NULL,
  `nachrep` longtext,
  `FD` varchar(45) DEFAULT '0',
  `TBKomm` longtext,
  `TDKomm` longtext,
  `FDKomm` longtext,
  `Gestrichen` varchar(45) DEFAULT '0',
  `HBKomm` longtext,
  `first` int(11) DEFAULT '0',
  `prior` int(11) DEFAULT '0',
  `next` int(11) DEFAULT '0',
  `erster` int(11) DEFAULT '0',
  `voheriger` int(11) DEFAULT '0',
  `naechster` int(11) DEFAULT '0',
  `weitergabe` int(11) DEFAULT '-1',
  `Firmenname` varchar(50) DEFAULT NULL,
  `Bild` blob,
  `debitor` varchar(45) DEFAULT NULL,
  `gruppe` int(11) DEFAULT '-1',
  `zegeplant` int(11) unsigned DEFAULT NULL,
  `frueher` int(11) DEFAULT '-1',
  `EWKomm` longtext,
  `TerminKomm` longtext,
  PRIMARY KEY (`Lfdnr`),
  KEY `Datum` (`Datum`,`Zeit`)
) ENGINE=InnoDB AUTO_INCREMENT=587 DEFAULT CHARSET=latin1;
Und hier mal mein SQL-Statement
Code:
Select Datum, Zeit, Wer, Arbeiten from main where REPLACE(KZ," ","")=:kennzeichen and lfdnr=erster
Das Replace ist leider notwendig, damit alle Schreibweise auch erfasst werden.
Dieter
9 von 10 Stimmen in meinem Kopf sagen ich bin nicht verrückt. Die 10. summt dazu die Melodie von Supermario Bros.
MfG Captnemo

Geändert von Captnemo (29. Sep 2011 um 08:03 Uhr)
  Mit Zitat antworten Zitat
FredlFesl

Registriert seit: 19. Apr 2011
293 Beiträge
 
Delphi 2009 Enterprise
 
#14

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

  Alt 29. Sep 2011, 08:07
Code:
Select Datum, Zeit, Wer, Arbeiten from main where REPLACE(KZ," ","")=:kennzeichen and lfdnr=erster
Das Replace ist leider notwendig, damit alle Schreibweise auch erfasst werden.
Jo, da muss man sich nicht wundern, wenns ein bisserl länger dauert.
Wieso achtest Du beim *schreiben* der Daten nicht auf die Schreibweise? Dann brauchst Du das komische 'REPLACE' bei der Abfrage nämlich nicht mehr.

Auch immer wieder gerne gefragt und durchaus interessant: Wer scrollt eigentlich durch die 40.000 Datensätze?
Das Bild hängt schief.
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#15

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

  Alt 29. Sep 2011, 08:45
Also als erstes heut morgen möchte ich gleich mal eine Kniefall für Sir Rufo machen. Das ich im Create gleich das Zeilobject mit übergeben kann, da wär ich im Leben nicht drauf gekommen. Wenn ich das so richtig verstanden habe, dann kann in der Create-Procedure dann ein Connection und Query object erzeugen. Das SQL-Statement könnte ich anstelle von afilename übergeben.
Und das Execute wird nur einmal ausgeführt.

Okay, ich frage mich grad noch, wann und wie sich der Thread dann zerstört? Und ich müßte die Objekte, die ich im Thread erzeuge ja auch noch freigeben. Dafür würde ich den destructor Destroy nehmen, wenn das geht.
Ja, alles was der Thread für sich erzeugt wird in diesem auch wieder freigegeben.

Der Thread selber wird dann wie folgt gestartet: (siehe auch den restlichen Quelltest in der zip)
Delphi-Quellcode:
MyThread := TMyThread.Create( {[Parameter],} True {CreateSuspended} );
MyThread.OnTerminated := ThreadIstAmEnde; { Eine Methode die beim Beenden des Threads aufgerufen wird }
MyThread.FreeOnTerminate := True; { Ist der Thread am Ende, dann soll er sich auch gleich vom Acker machen }
MyThread.Start; { Jetzt geht es los }
Zur Tabelle hier das das Create
Code:
CREATE TABLE `main` (
  `Lfdnr` int(10) unsigned NOT NULL AUTO_INCREMENT,
  ...
  `KZ` varchar(45) DEFAULT NULL,
  ...
  PRIMARY KEY (`Lfdnr`),
  KEY `Datum` (`Datum`,`Zeit`)
) ENGINE=InnoDB AUTO_INCREMENT=587 DEFAULT CHARSET=latin1;
Leg doch auf das Feld KZ einen Index
Und hier mal mein SQL-Statement
Code:
Select Datum, Zeit, Wer, Arbeiten from main where REPLACE(KZ," ","")=:kennzeichen and lfdnr=erster
Das Replace ist leider notwendig, damit alle Schreibweise auch erfasst werden.
Und dann das Select so
Code:
Select Datum, Zeit, Wer, Arbeiten from main where lfdnr=erster AND REPLACE(KZ," ","")=:kennzeichen
Ja und am besten für das Feld KZ einen Trigger (Insekt und Update) erstellen, der die Leerzeichen schon direkt beim Eintrag in die DB entfernt.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Benutzerbild von Captnemo
Captnemo

Registriert seit: 27. Jan 2003
Ort: Bodenwerder
1.126 Beiträge
 
Delphi XE4 Architect
 
#16

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

  Alt 29. Sep 2011, 08:49
Wieso achtest Du beim *schreiben* der Daten nicht auf die Schreibweise? Dann brauchst Du das komische 'REPLACE' bei der Abfrage nämlich nicht mehr.

Auch immer wieder gerne gefragt und durchaus interessant: Wer scrollt eigentlich durch die 40.000 Datensätze?
Leider muß ich, was die Schreibweise angeht, davon ausgehen, dass sie eben nicht immer absolut im gleichen format vorhanden ist. Sonst hätte ich das nicht so machen müssen.

Kein Scrollt durch 40000 Datensätze. Darum ja auch die Abfrage. Bleiben ja nur so 20 übrig.
Dieter
9 von 10 Stimmen in meinem Kopf sagen ich bin nicht verrückt. Die 10. summt dazu die Melodie von Supermario Bros.
MfG Captnemo
  Mit Zitat antworten Zitat
Benutzerbild von Captnemo
Captnemo

Registriert seit: 27. Jan 2003
Ort: Bodenwerder
1.126 Beiträge
 
Delphi XE4 Architect
 
#17

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

  Alt 29. Sep 2011, 08:59
@Sir Rufo: Ich hab jetzt mal versucht, den Code von dir zu übernehmen. Leider stoße ich da mal wieder an meine Grenzen. Das Projekt ist ein D7 geschrieben. Und teile deines Codes funktionieren unter D7 noch nicht.
Im Grund sollte das kein Problem sein, aber durch meine begrenzte Erfahrung mit Threads bedeuten die Änderungen für mich schon ein Problem.

Zum einen sind da Sachen wie:

Generics.Collections -> Kennt D7 ja nicht.

ebenso dieses: TList<TStrings>.Create und for lField in FData.Fields do (ich denke for .. in kennt D7 auch nicht).

Aber das krieg ich schon hin.

Aber hiermit hab ich wirklich probleme:

Synchronize( procedure begin SendDataToListView( AListView, lItems, Sync ); end ) geht wohl so in D7 auch noch nicht.

Ich kann jetzt aber auch nicht mit dem Projekt auf D2010 umziehen.
Dieter
9 von 10 Stimmen in meinem Kopf sagen ich bin nicht verrückt. Die 10. summt dazu die Melodie von Supermario Bros.
MfG Captnemo
  Mit Zitat antworten Zitat
Benutzerbild von Union
Union

Registriert seit: 18. Mär 2004
Ort: Luxembourg
3.492 Beiträge
 
Delphi 7 Enterprise
 
#18

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

  Alt 29. Sep 2011, 09:13
Das ist doch nur eine Inline Definition. Schreib die zu synchronisierende procedure einfach normal und ruf sie dort auf. Also aus:
Synchronize( procedure begin SendDataToListView( AListView, lItems, Sync ); end ) wird dann
Delphi-Quellcode:
procedure TMeinThread.SyncProc
begin
   SendDataToListView( AListView, lItems, Sync );
end;

...

Synchronize(SyncProc)
Das ganze hier ist aber aus meiner Sicht komplett überflüssig, da das Problem in der Datenbankdefinition und der Abfrage liegt. Du treibst hier einen affenartigen Aufwand um Kosmetik zu betreiben.

Dein KennZeichen Feld sollte normalisiert werden. Wo kommt der Abfrage Parameter her? Und wie sieht die Mastertabelle aus?
Ibi fas ubi proxima merces
sudo /Developer/Library/uninstall-devtools --mode=all
  Mit Zitat antworten Zitat
jobo

Registriert seit: 29. Nov 2010
3.072 Beiträge
 
Delphi 2010 Enterprise
 
#19

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

  Alt 29. Sep 2011, 09:58
Ich kann mich nur union anschließen.

Statt die Ursache in der Datenhaltung zu beseitigen, wird das "Problem" in einem Thread versteckt.

Wieviel Nutzer verwenden diese Maske? Wie oft am Tag?
Sicher, es sind nur ein paar Sekunden pro Aufruf. Aber ich vermute, dass Euer Hardwarehandler vielleicht bald mit einem großen Paket vorbeikommt, wenn Du den Stil beibehälst.
Gruß, Jo
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#20

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

  Alt 29. Sep 2011, 10:30
Ok, unter D7 ist das so tatsächlich nicht möglich

Mal sehen ob ich da eine D7-taugliche Version dir bereitstellen kann.

Allerdings solltest du tatsächlich mal das Kennzeichen in den Index aufnehmen und mit einem Trigger sicherstellen, dass dort keine Leerzeichen auftauchen.

Dann geht diese Abfrage bestimmt wie der Teufel
Code:
Select Datum, Zeit, Wer, Arbeiten from main where lfdnr = erster AND KZ = :kennzeichen
EDIT: Da fällt mir doch gerade auf, dass du das Feld erster auch in den Index aufnehmen solltest
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 3     12 3      


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 05:17 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