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

2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von DL3AD »

Hallo,

In meinem Projekt verende ich die ZEOS Komponenten in verbindung mit einer SQlite3 Datenbank.
ich möchte eine größere Datenmenge möglichst schnell in eine SQlite3 DB schreiben.
Es wird eine Tabelle mit 23 Feldern beschrieben.
So habe ich es zuerst gemacht mit CachedUpdates

Code: Alles auswählen

Query.insert;
.
.
.
Query.post;
//Wenn alle Datensätze im Cache dann
Query.applyupdates;

Auch ohne CachedUpdates geht es nicht schneller.
Das Cachen der Datensätze geht schnell ca 0,3s - das Updaten braucht dann etliche sekunden (ca 8s).

Wie kann ich es mit den ZEOS Werkzeugen besser machen ?

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 »

viewtopic.php?f=55&t=10862&start=34
viewtopic.php?f=55&t=10862&start=39
viewtopic.php?f=55&t=10862&start=58 (Hinweis bezüglich cached updates)
Vielleicht könnte der TZSQLProzessor die tsqlstatement ersetzen. Ich habe mit dieser Komponente noch nie gearbeitet.
Edit: Nein, das sieht nicht schnell aus:

Code: Alles auswählen

 
procedure TZSQLProcessor.Execute;
var
  I: Integer;
  Statement: IZPreparedStatement;
  Action: TZErrorHandleAction;
  SQL: TZSQLStrings;
begin
  if Connection = nil then
    raise EZDatabaseError.Create(SConnectionIsNotAssigned);
 
  FConnection.ShowSQLHourGlass;
  try
    SQL := TZSQLStrings.Create;
    SQL.Dataset := Self;
    SQL.ParamCheck := FScript.ParamCheck;
    SQL.MultiStatements := False;
    Parse;
 
    for I := 0 to Pred(StatementCount) do
    begin
      Action := eaSkip;
      DoBeforeExecute(I);
      repeat
        try
          SQL.Text := GetStatement(I);
{http://zeos.firmos.at/viewtopic.php?t=2885&start=0&postdays=0&postorder=asc&highlight=}
          if SQL.StatementCount > 0 then
            begin
              Statement := CreateStatement(SQL.Statements[0].SQL, nil);
              SetStatementParams(Statement, SQL.Statements[0].ParamNamesArray,
                FParams);
              Statement.ExecuteUpdatePrepared;
            end;
          Statement := nil;
        except
          on E: Exception do
          begin
            if Assigned(Statement) then
              Statement := nil;
            Action := DoOnError(I, E);
            if Action = eaFail then
              RaiseSQLException(E)
            else if Action = eaAbort then
              Exit;
          end;
        end;
      until Action <> eaRetry;
      DoAfterExecute(I);
 
    end;
  finally
    FreeAndNil(SQL);
    Connection.HideSQLHourGlass;
  end;
end;
 
Zuletzt geändert von mse am Di 11. Jul 2017, 13:07, insgesamt 2-mal geändert.

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,

ich habe es mit und ohne CachedUpdates probiert.
Ich verwende LAZARUS IDE und die ZEOS Komponenten d.h. ich benötige einen Hinweis wie ich es mit diesen Werkzeugen machen kann !

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:Hallo mse,

ich habe es mit und ohne CachedUpdates probiert.
Ich verwende LAZARUS IDE und die ZEOS Komponenten d.h. ich benötige einen Hinweis wie ich es mit diesen Werkzeugen machen kann !

Gruß Frank

Hast du TZConnection.AutoCommit auf "false" gesetzt wie empfohlen? Bist du sicher, dass alle inserts in einer Transaktion geschrieben werden?
Zuletzt geändert von mse am Di 11. Jul 2017, 13:56, insgesamt 1-mal geändert.

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 »

TZQuery hat ExecSQL(), das sieht schneller aus:

Code: Alles auswählen

 
procedure TZAbstractRODataset.ExecSQL;
begin
  if Active then
    begin
      Connection.ShowSQLHourGlass;
      try
        Close;
      finally
        Connection.HideSQLHourGlass;
      end;
    end;
 
  Prepare;
 
  Connection.ShowSQLHourGlass;
  try
    SetStatementParams(Statement, FSQL.Statements[0].ParamNamesArray,
      FParams, FDataLink);
 
    FRowsAffected := Statement.ExecuteUpdatePrepared;
  finally
    Connection.HideSQLHourGlass;
  end;
end;
 

Auch hier kannst du dich nach meinem Beispiel richten.

MmVisual
Beiträge: 1466
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 3.0 FPC 3.2)
CPU-Target: 32/64Bit

Re: 2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von MmVisual »

Man muss SQLite mitteilen dass es nicht bei jedem Datensatz in die Datei schreiben soll:

TZQuery.SQL.Text := 'BEGIN EXCLUSIVE TRANSACTION';
TZQuery.SQL.ExecSQL;

::: schreibe ...

TZQuery.SQL.Text := 'COMMIT TRANSACTION';
TZQuery.SQL.ExecSQL;

so einfach ist das.
EleLa - Elektronik Lagerverwaltung - www.elela.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 »

...TZConnection.AutoCommit ist auf "false"

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 »

Dann vermute ich, dass Zeos keine Transaktion öffnet und man es manuell machen muss wie von MmVisual geschrieben.
Edit: oder mit TZConnection.StartTransaction()?

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 MmVisual
... habe es so versucht - wird aber gemäckert(schmiert ab)

Code: Alles auswählen

  if AWrite then 
  begin
    Form1.QueryLog.SQL.Text:='BEGIN EXCLUSIVE TRANSACTION';
    Form1.QueryLog.ExecSQL;
 
    Form1.QueryLog.Insert;
    Form1.QueryLog.FieldByName('Callsign').AsString := ACALL;
    Form1.QueryLog.FieldByName('Qth').AsString      := AQTH;
    Form1.QueryLog.FieldByName('Name').AsString     := ANAME;
    Form1.QueryLog.FieldByName('Utc').AsString      := ATIME_ON;
    Form1.QueryLog.FieldByName('Date').AsString     := AQSO_DATE;
    Form1.QueryLog.FieldByName('RstTx').AsString    := ARST_SENT;
    Form1.QueryLog.FieldByName('RstRx').AsString    := ARST_RCVD;
    Form1.QueryLog.FieldByName('Band').AsString     := ABAND;
    Form1.QueryLog.FieldByName('Qrg').AsString      := AFREQ;
    Form1.QueryLog.FieldByName('Mode').AsString     := AMODE;
    Form1.QueryLog.FieldByName('Continent').AsString:= ACONT;
    Form1.QueryLog.FieldByName('Itu').AsString      := AITUZ;
    Form1.QueryLog.FieldByName('CQ').AsString       := ACQZ;
    Form1.QueryLog.FieldByName('Dxcc').AsString     := ADXCC;
    Form1.QueryLog.FieldByName('Country').AsString  := ACOUNTRY;
    Form1.QueryLog.FieldByName('Loc').AsString      := AGRIDSQUARE;
    Form1.QueryLog.FieldByName('Iota').AsString     := AIOTA;
    Form1.QueryLog.FieldByName('State').AsString    := ASTATE;
    Form1.QueryLog.FieldByName('Qsl').AsString      := AQSL_RCVD;
    Form1.QueryLog.FieldByName('Lotw').AsString     := ALOTW_QSL_RCVD;
    Form1.QueryLog.FieldByName('Eqsl').AsString     := AEQSL_QSL_RCVD;
    Form1.QueryLog.FieldByName('Memo').AsString     := ACOMMENT;
    Form1.QueryLog.FieldByName('Pfx').AsString      := APFX;
    Form1.QueryLog.Post;
  end
  else//AWrite true Daten schreiben bestätigen
  begin
    Form1.QueryLog.SQL.Text:='COMMIT TRANSACTION';
    Form1.QueryLog.ExecSQL;
  end;
end;                                         


Was habe ich da falsch gemacht ?

MmVisual
Beiträge: 1466
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 3.0 FPC 3.2)
CPU-Target: 32/64Bit

Re: 2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von MmVisual »

start der Transaktion nur 1 x
dann 2500 x Datesätze einfügen
dann Commiten
EleLa - Elektronik Lagerverwaltung - www.elela.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 »

funktioniert nicht :shock:
habe dass 1x vorab gemacht

Code: Alles auswählen

    Form1.QueryLog.SQL.Text:='BEGIN EXCLUSIVE TRANSACTION';
    Form1.QueryLog.ExecSQL;


dass dann 2500 mal

Code: Alles auswählen

 
    Form1.QueryLog.Insert;
    Form1.QueryLog.FieldByName('Callsign').AsString := ACALL;
    Form1.QueryLog.FieldByName('Qth').AsString      := AQTH;
    Form1.QueryLog.FieldByName('Name').AsString     := ANAME;
    Form1.QueryLog.FieldByName('Utc').AsString      := ATIME_ON;
    Form1.QueryLog.FieldByName('Date').AsString     := AQSO_DATE;
    Form1.QueryLog.FieldByName('RstTx').AsString    := ARST_SENT;
    Form1.QueryLog.FieldByName('RstRx').AsString    := ARST_RCVD;
    Form1.QueryLog.FieldByName('Band').AsString     := ABAND;
    Form1.QueryLog.FieldByName('Qrg').AsString      := AFREQ;
    Form1.QueryLog.FieldByName('Mode').AsString     := AMODE;
    Form1.QueryLog.FieldByName('Continent').AsString:= ACONT;
    Form1.QueryLog.FieldByName('Itu').AsString      := AITUZ;
    Form1.QueryLog.FieldByName('CQ').AsString       := ACQZ;
    Form1.QueryLog.FieldByName('Dxcc').AsString     := ADXCC;
    Form1.QueryLog.FieldByName('Country').AsString  := ACOUNTRY;
    Form1.QueryLog.FieldByName('Loc').AsString      := AGRIDSQUARE;
    Form1.QueryLog.FieldByName('Iota').AsString     := AIOTA;
    Form1.QueryLog.FieldByName('State').AsString    := ASTATE;
    Form1.QueryLog.FieldByName('Qsl').AsString      := AQSL_RCVD;
    Form1.QueryLog.FieldByName('Lotw').AsString     := ALOTW_QSL_RCVD;
    Form1.QueryLog.FieldByName('Eqsl').AsString     := AEQSL_QSL_RCVD;
    Form1.QueryLog.FieldByName('Memo').AsString     := ACOMMENT;
    Form1.QueryLog.FieldByName('Pfx').AsString      := APFX;
    Form1.QueryLog.Post;
 

und zum Abschluss dass

Code: Alles auswählen

 Form1.QueryLog.SQL.Text:='COMMIT TRANSACTION';
    Form1.QueryLog.ExecSQL;


Was ist daran Falsch ?

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 »

Für die transaction statements darf man nicht die TZQuery der Tabelle ("QueryLog") benutzen. Alternativ probiere TZConnection.StartTransaction() und TZConnection.Commit().

MmVisual
Beiträge: 1466
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 3.0 FPC 3.2)
CPU-Target: 32/64Bit

Re: 2500 Datensätze schnell in eine SQlite3 schreiben

Beitrag von MmVisual »

Nach dem Start der Transaktion ist die Form1.QueryLog geschlossen und du muss die erneut mit einem SQL SELECT Statement füllen und öffnen.
EleLa - Elektronik Lagerverwaltung - www.elela.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 »

... so nach dem hier

Code: Alles auswählen

  QueryLog.SQL.Text:='BEGIN EXCLUSIVE TRANSACTION';
  QueryLog.ExecSQL;

habe ich dass gemacht

Code: Alles auswählen

QueryLog.SQL.Text:='Select * from Log';
QueryLog.Open

und dann dass 2500 mal

Code: Alles auswählen

    Form1.QueryLog.Insert;
    Form1.QueryLog.FieldByName('Callsign').AsString := ACALL;
    Form1.QueryLog.FieldByName('Qth').AsString      := AQTH;
    Form1.QueryLog.FieldByName('Name').AsString     := ANAME;
    Form1.QueryLog.FieldByName('Utc').AsString      := ATIME_ON;
    Form1.QueryLog.FieldByName('Date').AsString     := AQSO_DATE;
    Form1.QueryLog.FieldByName('RstTx').AsString    := ARST_SENT;
    Form1.QueryLog.FieldByName('RstRx').AsString    := ARST_RCVD;
    Form1.QueryLog.FieldByName('Band').AsString     := ABAND;
    Form1.QueryLog.FieldByName('Qrg').AsString      := AFREQ;
    Form1.QueryLog.FieldByName('Mode').AsString     := AMODE;
    Form1.QueryLog.FieldByName('Continent').AsString:= ACONT;
    Form1.QueryLog.FieldByName('Itu').AsString      := AITUZ;
    Form1.QueryLog.FieldByName('CQ').AsString       := ACQZ;
    Form1.QueryLog.FieldByName('Dxcc').AsString     := ADXCC;
    Form1.QueryLog.FieldByName('Country').AsString  := ACOUNTRY;
    Form1.QueryLog.FieldByName('Loc').AsString      := AGRIDSQUARE;
    Form1.QueryLog.FieldByName('Iota').AsString     := AIOTA;
    Form1.QueryLog.FieldByName('State').AsString    := ASTATE;
    Form1.QueryLog.FieldByName('Qsl').AsString      := AQSL_RCVD;
    Form1.QueryLog.FieldByName('Lotw').AsString     := ALOTW_QSL_RCVD;
    Form1.QueryLog.FieldByName('Eqsl').AsString     := AEQSL_QSL_RCVD;
    Form1.QueryLog.FieldByName('Memo').AsString     := ACOMMENT;
    Form1.QueryLog.FieldByName('Pfx').AsString      := APFX;
    Form1.QueryLog.Post;         

und zum Abschluss dass

Code: Alles auswählen

  QueryLog.SQL.Text:='COMMIT TRANSACTION';
  QueryLog.ExecSQL;


er schmiert zwar nicht ab aber es kommt nix in der DB an :shock:

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:

Code: Alles auswählen

QueryLog.SQL.Text:='Select * from Log';
QueryLog.Open


Hinweis: das ist nicht ideal, da alle bestehenden Logeinträge geladen werden. Das kann bei 10 Mio QSO's dauern. ;-)

Antworten