2500 Datensätze schnell in eine SQlite3 schreiben

Für Themen zu Datenbanken und Zugriff auf diese. Auch für Datenbankkomponenten.
DL3AD
Beiträge: 478
Registriert: Fr 13. Sep 2013, 12:07
OS, Lazarus, FPC: Debian Bullseye (L 2.2.0)
CPU-Target: 64Bit
Wohnort: Rügen

Re: 2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von DL3AD »

Hallo,

nun funktioniert es - CachedUpdates war noch aktiviert habe ich raus genommen und nun dauert es 0,68s für 2468 Datensätze incl Parsen aus einer ADIF Datei.
Vielen Dank an alle die mir geholfen haben !
Ich habe dank euch sehr viel gelernt!

Gruß Frank

DL3AD
Beiträge: 478
Registriert: Fr 13. Sep 2013, 12:07
OS, Lazarus, FPC: Debian Bullseye (L 2.2.0)
CPU-Target: 64Bit
Wohnort: Rügen

Re: 2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von DL3AD »

Hallo,

es gibt doch noch ein Problem.
Wenn die Datenbanktabelle leer ist dann funktioniert der Import sauber.
Wenn jedoch Daten bereits vorhanden sind dann schmiert er ab.

Woran könnte es liegen ?

Gruß Frank
Dateianhänge
Fehler.png

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: 2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von mse »

Vermutlich zweimal dieselben Daten eingelesen, dann gibt es einen Fehler im primary index -> es dürfen keine records mit identischen callsign,utc,date,band und mode Feldern existieren.

DL3AD
Beiträge: 478
Registriert: Fr 13. Sep 2013, 12:07
OS, Lazarus, FPC: Debian Bullseye (L 2.2.0)
CPU-Target: 64Bit
Wohnort: Rügen

Re: 2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von DL3AD »

Hallo mse,

genau dass war es - andere Daten hinterhergeschoben funktioniert. DANKE!

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Re: 2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von Christian »

Code: Alles auswählen

SELECT * FROM log LIMIT 1;
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

DL3AD
Beiträge: 478
Registriert: Fr 13. Sep 2013, 12:07
OS, Lazarus, FPC: Debian Bullseye (L 2.2.0)
CPU-Target: 64Bit
Wohnort: Rügen

Re: 2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von DL3AD »

... wie kann man es sinnvoll abfangen dass das Programm nicht abschmiert wenn versucht wird gleiche Daten einzulesen ?

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: 2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von mse »

Das kommt darauf an, wie du im Falle von doppelten primary keys verfahren willst.
Im erwähnten Beispiel mache ich im Fehlerfall einen "rollback" statt "commit", das heisst die ganze neue QSL-Liste wird verworfen.
https://gitlab.com/mseide-msegui/mseuni ... s/main.pas
https://gitlab.com/mseide-msegui/mseuni ... module.pas
Rollback gibt es auch in Zeos (TZConnection.Rollback) oder direkt als SQL
https://sqlite.org/lang_transaction.html

DL3AD
Beiträge: 478
Registriert: Fr 13. Sep 2013, 12:07
OS, Lazarus, FPC: Debian Bullseye (L 2.2.0)
CPU-Target: 64Bit
Wohnort: Rügen

Re: 2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von DL3AD »

... :shock:
gibt es da nicht was, das die ZQuery mäckert mit einer sinnvollen Meldung, dass man dann ein Rollback machen kann ?

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: 2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von wp_xyz »

DL3AD hat geschrieben:... wie kann man es sinnvoll abfangen dass das Programm nicht abschmiert wenn versucht wird gleiche Daten einzulesen ?

Vor dem Schreiben eines Records prüfen, ob er schon existiert?

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: 2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von mse »

DL3AD hat geschrieben:gibt es da nicht was, das die ZQuery mäckert mit einer sinnvollen Meldung, dass man dann ein Rollback machen kann ?

Fehler werden als exception gemeldet (du bezeichnest das als "abschmieren").
https://www.freepascal.org/docs-html/cu ... 6-24800017
Bei mir sieht es so aus:
exception.png

Ich fange die exception mit "except" ab, mache den rollback und werfe die exception nochmals mit "raise", damit der Nutzer davon erfährt, siehe Beispiel. Was möchtest du mit doppelten Einträgen machen?

DL3AD
Beiträge: 478
Registriert: Fr 13. Sep 2013, 12:07
OS, Lazarus, FPC: Debian Bullseye (L 2.2.0)
CPU-Target: 64Bit
Wohnort: Rügen

Re: 2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von DL3AD »

Hallo,

ich habe mal bei einem Anderen Programm (Windows auf .Net basis) welches auch den ADTF import macht angeschaut wie es reagiert wenn man Datensätze Doppelt einließt.
Es werden in einem vermutlich Memo genau die Fehlermeldungen untereinander ausgegeben wie in deinem Post zuvor gezeigt.
D.h. es wird versucht den Datensatz zu schreiben - klappt es nicht Fehlermeldung ins Memo und weiter mit dem nächsten.

Meine Struktur zum schweiben der Datensätze sieht so aus

Code: Alles auswählen

  QueryLog.SQL.Text:='BEGIN EXCLUSIVE TRANSACTION';
  QueryLog.ExecSQL;
  QueryLog.SQL.Text:='SELECT * FROM log LIMIT 1';
  QueryLog.Open;
  while (ADIFPars.Ai < Length(ADIFPars.Txt)) do//Importschleife
  begin
    GetRecord(ACALL,AQTH,ANAME,ATIME_ON,AQSO_DATE,ARST_SENT,ARST_RCVD,ABAND,
              AFREQ,AMODE,ACONT,AITUZ,ACQZ,ADXCC,ACOUNTRY,AGRIDSQUARE,AIOTA,
              ASTATE,AQSL_RCVD,ALOTW_QSL_RCVD,AEQSL_QSL_RCVD,ACOMMENT,APFX);
    if ACALL <> '' then
    begin
      WriteDB;//Datensätze schreiben
      inc(n); //QSO Zähler erhöhen
    end;
  end;
  QueryLog.SQL.Text:='COMMIT TRANSACTION';
  QueryLog.ExecSQL;         

Wie muss hier try except und raise rein um die Meldungen in einem Memo zu listen ?

Gruß Frank

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: 2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von mse »

DL3AD hat geschrieben:Wie muss hier try except und raise rein um die Meldungen in einem Memo zu listen ?

Vermutlich rund um <TZQuery>.Post(). Wie bereits mehrmals erwähnt würde ich nicht Insert() und Post() sondern <TZReadonlyQuery>.ExecSQL() mit einem entsprechenden INSERT statement verwenden.
Im erweiterten Beispiel
https://gitlab.com/mseide-msegui/mseuni ... ertrecords
sieht es so aus:

Code: Alles auswählen

 
function tdbmo.writelogrec(const logrec: adlogrecty): boolean;
var
 f1: adiftagty;
begin
 result:= false;
 for f1:= firstlogfield to lastlogfield do begin
  fparams[f1].asnullmsestring:= logrec.fields[f1];
 end;
 try
  insertstatement.execute();
  result:= true; //im fehlerfall wird diese zeile nicht erreicht
 except
  on e: esqlite3error do begin
   flasterror:= e.errormessage;
  end;
  else begin
   raise; //not expected error
  end;
 end;
end;
 

Was Zeos für eine exception-Klasse wirft müsste man nachsehen. Was sicher funktioniert ist

Code: Alles auswählen

 
  on e: exception do begin
   flasterror:= e.message;
  end;
 

da alle Exception-Klassen von "exception" erben.
Zur Vollständigkeit noch die Hauptroutine:

Code: Alles auswählen

 
procedure tmainfo.testexe(const sender: TObject);
var
 rec1: adlogrecty;
 t1,t2: tdatetime;
 i1,i2: int32;
 f1: adiftagty;
begin
 for f1:= firstlogfield to lastlogfield do begin
  rec1.fields[f1]:= msestring(adiftagnames[f1]);
 end;
 t1:= nowutc();
 rec1.fields[at_qso_date]:= datetoadif(t1);
 rec1.fields[at_time_on]:= timetoadif(t1);
 dbmo.init();
 errordisp.clear();
 i2:= errored.value;
 if i2 > 0 then begin
  i2:= reccounted.value div i2;
 end;
 t1:= nowutc();
 try
  for i1:= 1 to reccounted.value do begin
   if (i2 = 0) or (i1 mod i2 <> 0) then begin //else produce an error
    rec1.fields[at_call]:= inttostrmse(i1);
   end;
   if not dbmo.writelogrec(rec1) then begin
    errordisp.appenddatarow(dbmo.lasterror);
   end;
  end;
  dbmo.commit();
 except
  dbmo.rollback();
  raise;
 end;
 t2:= nowutc();
 runtimedi.value:=  (t2-t1)*24*60*60;
 importcountdisp.value:= reccounted.value-errordisp.rowcount;
end;
 
Dateianhänge
exception1.png

DL3AD
Beiträge: 478
Registriert: Fr 13. Sep 2013, 12:07
OS, Lazarus, FPC: Debian Bullseye (L 2.2.0)
CPU-Target: 64Bit
Wohnort: Rügen

Re: 2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von DL3AD »

Hallo mse,

es ist ja nett das du mir helfen willst Danke dafür - aber ich sehe in deinem code nicht durch - sicherlich kann man verschiedene Dinge ander und besser machen - aber so weit bin ich noch nicht.
Ich habe extra meinen Code gepostet.
Wie muss hier try except und raise rein um die Meldungen in einem Memo zu listen ?

Gruß Frank

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: 2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von wp_xyz »

DL3AD hat geschrieben:

Code: Alles auswählen

  QueryLog.SQL.Text:='BEGIN EXCLUSIVE TRANSACTION';
  QueryLog.ExecSQL;
  QueryLog.SQL.Text:='SELECT * FROM log LIMIT 1';
  QueryLog.Open;
  while (ADIFPars.Ai < Length(ADIFPars.Txt)) do//Importschleife
  begin
    GetRecord(ACALL,AQTH,ANAME,ATIME_ON,AQSO_DATE,ARST_SENT,ARST_RCVD,ABAND,
              AFREQ,AMODE,ACONT,AITUZ,ACQZ,ADXCC,ACOUNTRY,AGRIDSQUARE,AIOTA,
              ASTATE,AQSL_RCVD,ALOTW_QSL_RCVD,AEQSL_QSL_RCVD,ACOMMENT,APFX);
    if ACALL <> '' then
    begin
      WriteDB;//Datensätze schreiben
      inc(n); //QSO Zähler erhöhen
    end;
  end;
  QueryLog.SQL.Text:='COMMIT TRANSACTION';
  QueryLog.ExecSQL;         

Wie muss hier try except und raise rein um die Meldungen in einem Memo zu listen ?


Ich weiß nicht, was WriteDB im Detail macht (das ist das Problem mit partiell gepostetem Code), aber dort wird wahrscheinlich die Exception auftreten. Also umgibst du diesen Aufruf mit try-except und schreibst im except-Fall die Meldung ins Memo. Also so:

Code: Alles auswählen

while (ADIFPars.Ai < Length(ADIFPars.Txt) do begin
  GetRecord(ACALL, ...);
  if ACALL <> '' then
  begin
    try
      WriteDB;
      inc(n);
    except
      on E:Exception do Memo1.Lines.Add(E.Message);
    end;
  end;
  QueryLog.SQL.Text := '....';
  QueryLog.ExecSQL;
...


Das Inc(n) wird hier im fall einer Exception nicht mehr ausgeführt. Falls das nicht so sein soll, musst du es aus dem try-except Block herausnehmen.

DL3AD
Beiträge: 478
Registriert: Fr 13. Sep 2013, 12:07
OS, Lazarus, FPC: Debian Bullseye (L 2.2.0)
CPU-Target: 64Bit
Wohnort: Rügen

Re: 2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von DL3AD »

Hallo wp_xyz,

vielen Dank !
Das war der passende Hinweis.
Wenn ich das richtig verstanden habe dann umschließt try und except den Programmteil der "abgesichert" werden soll
und zwischen except und dem end kommt rein was man im Fehlerfall macht ?
Wozu ist raise und finaly da ?

Gruß Frank

Antworten