Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Insert Optimierung (https://www.delphipraxis.net/166857-insert-optimierung.html)

Franzelchen 2. Mär 2012 17:16

Datenbank: firebird • Version: 2.1 • Zugriff über: zeos

Insert Optimierung
 
Hallo,

Den folgenden Quelltext habe ich im Forum gefunden und für meinen Zweck angepaßt:
Delphi-Quellcode:
procedure TForm1.InputDatasetClick(Sender: TObject);
var               i:Integer;
 startzeit,stopzeit : TDateTime;

begin
startzeit := now;
Screen.Cursor := crHourGlass;
ZConnection.Database := 'c:\msql\firedb.fdb';

 for i:=0 to SG1.RowCount-1 do
  begin
   ZQuery1.SQL.text:='INSERT INTO TestTabelle (Wert1, Wert2) VALUES(:v1, :v2)';
        with ZQuery1.params do
    begin
      ParamValues['v1']:=SG1.Cells[0, i];
      ParamValues['v2']:=SG1.Cells[1, i];
    end; // with
   ZQuery1.ExecSQL;
  end; //  i
Screen.Cursor := crDefault;
stopzeit := now;
Panel1.Caption :='SuchZeit : '+ FormatDateTime('nn:ss:zzz', StopZeit - StartZeit) ;
end;

Das Übertragen von ca. 35000 Datensätzen dauert ca. 4 Minuten.

Kann die Übertragung beschleunigt werden?

Ich habe mich natürlich über einige andere Möglichkeiten informiert.
zB. Bulk insert wird aber von Firebird leider nicht unterstützt.

oder external tables bei denen Sicherheitsbedenken bestehen? Welche das sind...?? daraus folgt vermutlich für mich nicht verwendbar.

Ich bitte um Eure Hilfe

himitsu 2. Mär 2012 17:33

AW: Insert Optimierung
 
Das
Delphi-Quellcode:
ZQuery1.SQL.Text := 'INSERT INTO TestTabelle (Wert1, Wert2) VALUES(:v1, :v2)';
reicht doch einmal aus, also vor der Schleife.

Und du kannst mal probieren, ob eine Zusammenfassung mehrere Werte noch ein bissl was rausholen kannst.
Delphi-Quellcode:
ZQuery1.SQL.Text := 'INSERT INTO TestTabelle (Wert1, Wert2) VALUES (:v0, :v1), (:v2, :v3), (:v4, :v5), (:v6, :v7);';
...
with ZQuery1.Params do begin
  ParamValues[0] := SG1.Cells[0, i];
  ParamValues[1] := SG1.Cells[1, i];
  ParamValues[2] := SG1.Cells[0, i + 1];
  ParamValues[3] := SG1.Cells[1, i + 1];
  ParamValues[4] := SG1.Cells[0, i + 2];
  ParamValues[5] := SG1.Cells[1, i + 2];
  ParamValues[6] := SG1.Cells[0, i + 3];
  ParamValues[7] := SG1.Cells[1, i + 3];
end;
ZQuery1.ExecSQL;
Mit vielen kleinen Inserts, haben wir in Postgres, mit PgDAC, etwa 60 bis maximal 250 Datensätze rausbekommen.
Mit deaktivierten Triggern und soeinem zusammengefassten Insert sind wir schon auf über 600 DS/s gekommen.

Das liegt aber irgendwie am Delphi, denn in C# soll es wesentlich schneller gehn ... ich glaub da was von über 10.000 DS/s gehört zu haben.

Wenn dei DBMS sowas wie einen COPY-Befehl kennt, worüber man z.B. die Daten via einer CSV-Datei direkt in die Datenbank reinbekommt, dann wäre das wohl der schnellste Weg.

p80286 2. Mär 2012 17:39

AW: Insert Optimierung
 
Du schleppst 35.000 datensätze in einem Stringgrid mit Dir herum?
Wie wäre es dafür eine Stringlist oder eine List zu nehmen?
Ein Stringgrid ist ja eigentlich für die Datenanzeige zuständig, nicht für die Datenhaltung. Da sollte noch etwas für Dich drin sein.

Gruß
K-H

himitsu 2. Mär 2012 17:51

AW: Insert Optimierung
 
Nunja, in einem TStringGrid stecken auch nur ein paar TStringLists.

Oder wie wäre es mit einem Memory-DataSet und einem DBGrid?

ConstantGardener 2. Mär 2012 18:04

AW: Insert Optimierung
 
...ausserdem solltest Du den Query Text vor dem For setzen und mit Prepare der Datenbank die Chance geben dieses Statement vorzubereiten. In der Schleife setzt du nur noch die Parameter. Das bringt dann einiges an Speed.

Franzelchen 2. Mär 2012 18:25

AW: Insert Optimierung
 
Zitat:

Zitat von himitsu (Beitrag 1154175)
Das
Delphi-Quellcode:
ZQuery1.SQL.Text := 'INSERT INTO TestTabelle (Wert1, Wert2) VALUES(:v1, :v2)';
reicht doch einmal aus, also vor der Schleife.

?? Wenn ich das vor die Schleife setze, wie wird dann die Parameterliste befü...

richtig!! egal wo INSERT steht,ausgeführt wird es mit execsql.

Delphi-Quellcode:
ZQuery1.SQL.Text := 'INSERT INTO TestTabelle (Wert1, Wert2) VALUES (:v0, :v1), (:v2, :v3), (:v4, :v5), (:v6, :v7);';
...
with ZQuery1.Params do begin
  ParamValues[0] := SG1.Cells[0, i];
  ParamValues[1] := SG1.Cells[1, i];
  ParamValues[2] := SG1.Cells[0, i + 1];
  ParamValues[3] := SG1.Cells[1, i + 1];
  ParamValues[4] := SG1.Cells[0, i + 2];
  ParamValues[5] := SG1.Cells[1, i + 2];
  ParamValues[6] := SG1.Cells[0, i + 3];
  ParamValues[7] := SG1.Cells[1, i + 3];
end;
ZQuery1.ExecSQL;
Genau das ist meines Verständnisses nach Bulk insert?
Packe eine Reihe Parameter in einen Bulk (=Paket) und überführe mit einem Insert das Paket.


Zitat:

Zitat von p80286 (Beitrag 1154177)
Du schleppst 35.000 datensätze in einem Stringgrid mit Dir herum?
Wie wäre es dafür eine Stringlist oder eine List zu nehmen?
Ein Stringgrid ist ja eigentlich für die Datenanzeige zuständig, nicht für die Datenhaltung. Da sollte noch etwas für Dich drin sein.

Gruß
K-H

Die Textdatei ist Tabulator getrennt. Komma, Punkt, Semikolon usw. sind Schriftzeichen innerhalb des Textes.

Stringlist hätte ich auch gern genommen, aber meine Programmierkenntnisse reichen dafür nicht aus.

Zitat:

Zitat von ConstantGardener (Beitrag 1154182)
...ausserdem solltest Du den Query Text vor dem For setzen und mit Prepare der Datenbank die Chance geben dieses Statement vorzubereiten. In der Schleife setzt du nur noch die Parameter. Das bringt dann einiges an Speed.

Prepare ist bereits im Quelltext von Zeos eingebaut.

hoika 2. Mär 2012 20:14

AW: Insert Optimierung
 
Hallo,

hat die Testtabelle irgendwelche Indizes ?
Die könnte man vor dem Insert deaktivieren.

Mit dem ZEOS und Prepare ist in der Tat so,
er prepared selber, allerdings darf sich der SQL-Text
nicht ändern, was hier durch Nutzung von Parametern
ja auch gut gemacht wird.


Heiko

Franzelchen 2. Mär 2012 20:57

AW: Insert Optimierung
 
Den ZQuery.text vor die Schleife zu ziehen, statt in der Schleife zu haben, wie in Post 1 bringt sage und schreibe rund 30 sec.

Jetzt habe ich die Parameterliste eingefügt und es passiert absolut nichts. Zeitmessung zeigt 0,00

Wo ist der Fehler?

Delphi-Quellcode:
var  i:Integer;
var  startzeit,stopzeit : TDateTime;

begin
startzeit := now;
Screen.Cursor := crHourGlass;
ZConnection.Database := 'c:\msql\firedb.fdb';
 Zquery1.SQL.Text:='INSERT INTO TestTabelle (Wert1, Wert2) VALUES(:v1, :v2), (:v3, :v4), (:v5, :v6), (:v7, :v8), (:v9, :v10)';
 while i <= SG1.RowCount-1  do
  begin
       with ZQuery1.params do
       begin
        ParamValues['v1']:=SG1.Cells[0, i];
        ParamValues['v2']:=SG1.Cells[1, i];
        ParamValues['v3'] := SG1.Cells[0, i + 1];
        ParamValues['v4'] := SG1.Cells[1, i + 1];
        ParamValues['v5'] := SG1.Cells[0, i + 2];
        ParamValues['v6'] := SG1.Cells[1, i + 2];
        ParamValues['v7'] := SG1.Cells[0, i + 3];
        ParamValues['v8'] := SG1.Cells[1, i + 3];
        ParamValues['v9'] := SG1.Cells[0, i + 4];
        ParamValues['v10']:= SG1.Cells[1, i + 4];

      end; // with
  ZQuery1.ExecSQL;
   i := i+5;
  end; //  i
Screen.Cursor := crDefault;
stopzeit := now;
Panel1.Caption :='SuchZeit : '+ FormatDateTime('nn:ss:zzz', StopZeit - StartZeit) ;
end;
Indizes existieren nicht,
Trigger (was auch immer das ist) auch nicht.

himitsu 2. Mär 2012 21:57

AW: Insert Optimierung
 
Jupp, eine Schleife mit dem zusammengefassten Statement und größeren Schritten
Der Vorteil an dem indizierten Indize und nicht den Namentlichen wäre, daß man eine innere Schleife befüllen könnte und so z.B. 100 Werte zusammenfassen könnte.
(müßte man mal ausprobieren, welche Größe halbwegs optimal ist)

und zum Schluß nicht vergessen, daß da noch ein paar Zeilen übrigbleiben können.


Entweder man bastelt das Insert-Statement als String zusammen, für die Restlichen und vorher einmal für alle Teile
oder man läßt den Rest über ein einfaches Insert, mit je einem der Restwerte, übertragen.

Furtbichler 3. Mär 2012 07:46

AW: Insert Optimierung
 
Viel schneller geht es, wenn Du die Daten in eine Textdatei packst und dann per bulk insert in deine DB kopierst. So sollte das in wenigen Sekunden erledigt sein.

Wenn Du unbedingt mit INSERT-Befehlen arbeiten musst, dann könnte es viel schneller sein, wenn Du dir das Skript zunächst in einer Stringliste zusammenbaust und dann als ein Befehl innerhalb einer Transaktion ausführen lässt.

Zitat:

Zitat von Franzelchen (Beitrag 1154213)
Wo ist der Fehler?

i wird nicht initialisiert

himitsu 3. Mär 2012 10:46

AW: Insert Optimierung
 
Beim Bulk-Insert mußt du aber aufpassen.

Klient und Datenbankserver müssen auf die selben Speicherplätze zugreifen und sollten womöglich auch noch über die selben Netzwerkadressen verfügen (abgesehn man nutzt im Klient einen Dateipfad, und gibt dann dem Server einen anderen Pfad mit, unter welchem er die Datei findet)

Bei Postgres-COPY könnte man zwar auch über STDIN die Daten übergeben, aber ich hatte nicht rausbekommen, ob und wie man das via einem ExecSql-Befehl durch ein Delphi-Programm mit übertragen kann.

Bernhard Geyer 3. Mär 2012 10:49

AW: Insert Optimierung
 
Zitat:

Zitat von Furtbichler (Beitrag 1154249)
Zitat:

Zitat von Franzelchen (Beitrag 1154213)
Wo ist der Fehler?

i wird nicht initialisiert

Von Vorteil ist wer lesen kann. Nicht umsonst gibt der Compiler Warnmeldungen aus. :stupid:

Franzelchen 3. Mär 2012 13:14

AW: Insert Optimierung
 
Delphi-Quellcode:
var i:Integer;
var startzeit,stopzeit : TDateTime;

begin
startzeit := now;
i := 0; //   Initialisierung von i
Screen.Cursor := crHourGlass;
ZConnection.Database := 'c:\msql\firedb.fdb';
 Zquery1.SQL.Text:='INSERT INTO TestTabelle (Wert1, Wert2) VALUES(:v1, :v2), (:v3, :v4), (:v5, :v6), (:v7, :v8), (:v9, :v10)';
 while i <= SG1.RowCount-1 do
  begin
       with ZQuery1.params do
       begin
        ParamValues['v1']:=SG1.Cells[0, i];
        ParamValues['v2']:=SG1.Cells[1, i];
        ParamValues['v3'] := SG1.Cells[0, i + 1];
        ParamValues['v4'] := SG1.Cells[1, i + 1];
        ParamValues['v5'] := SG1.Cells[0, i + 2];
        ParamValues['v6'] := SG1.Cells[1, i + 2];
        ParamValues['v7'] := SG1.Cells[0, i + 3];
        ParamValues['v8'] := SG1.Cells[1, i + 3];
        ParamValues['v9'] := SG1.Cells[0, i + 4];
        ParamValues['v10']:= SG1.Cells[1, i + 4];

      end; // with
  ZQuery1.ExecSQL;
   i := i+5;
  end; // i
Screen.Cursor := crDefault;
stopzeit := now;
Panel1.Caption :='SuchZeit : '+ FormatDateTime('nn:ss:zzz', StopZeit - StartZeit) ;
end;
Das Problem dabei: Wo vorher keine Fehlermeldung war, erscheint jetzt

Code:
Benachrichtigung über Debugger-Exception
---------------------------
Im Projekt Project2.exe ist eine Exception der Klasse EZSQLException aufgetreten. Meldung: 'SQL Error: Dynamic SQL Error SQL error code = -104 Token unknown - line 1, column 51 ,. Error Code: -104. Invalid token The SQL: INSERT INTO Testtabelle (Wert1, Wert2) VALUES(?, ?), (?, ?), (?, ?), (?, ?), (?, ?); '. Prozeß wurde angehalten. Mit Einzelne Anweisung oder Start fortsetzen.
---------------------------
OK  Hilfe  
---------------------------
Spalte 51?? Es gibt nur 2 Spalten.

Bitte um Übersetzung dieser Fehlermeldung.


@ Furtbichler @ Himitsu

bulk insert Erkläre mir einer diesen Begriff. Ich meine gelesen zu haben, daß bulk insert bei Firebird nicht möglich ist.
Der Link von Furtbichler verweist auf externalTables. Um dies nutzen zu können, muß ich doch mir unbekannte Veränderungen an der firebird.conf Datei vornehmen!??

Zitat:

Zitat von himitsu (Beitrag 1154264)
Beim Bulk-Insert mußt du aber aufpassen.

Klient und Datenbankserver müssen auf die selben Speicherplätze zugreifen und sollten womöglich auch noch über die selben Netzwerkadressen verfügen (abgesehn man nutzt im Klient einen Dateipfad, und gibt dann dem Server einen anderen Pfad mit, unter welchem er die Datei findet)

Klient und Server sind auf ein und demselben Rechner. Reicht das?

himitsu 3. Mär 2012 13:22

AW: Insert Optimierung
 
Joar, wenn dann noch die Zugriffsrechte auf das Verzeichnis passend sind.
Bei uns auf'm Server hab ich ein privates virtuelles SUBST-Laufwerk, wo meine Verzeichnisse reingemappt sind ... der SQL-Server hat auf diese nicht direkt Zugriff drauf, auf dieses Arbeits-Laufwerk, außer über den originalen Pfad.


Nicht Spalte 51, sondern Zeichen 51.
Das ist auf den SQL-Text bezogen > Zeile und Spalte/Zeichen/Buchstabe.
Die schließende Klammer ) nach dem ersten Wertepaar? :gruebel:

[edit]
Oder das Komma danach? :gruebel:
http://www.firebirdsql.org/refdocs/l...21-insert.html
Eventuell kann Firebird das nicht? :shock:

Vielleicht dann einfach so. (Das andere wäre halt kürzer)
SQL-Code:
INSERT INTO Testtabelle (Wert1, Wert2) VALUES (?, ?);
INSERT INTO Testtabelle (Wert1, Wert2) VALUES (?, ?);
INSERT INTO Testtabelle (Wert1, Wert2) VALUES (?, ?);
INSERT INTO Testtabelle (Wert1, Wert2) VALUES (?, ?);
INSERT INTO Testtabelle (Wert1, Wert2) VALUES (?, ?);
Es geht ja einfach nur darum, den Traffik und die Anzahl der Anfragen an den Server zu verringern.

Franzelchen 3. Mär 2012 14:02

AW: Insert Optimierung
 
So etwa??

Delphi-Quellcode:
procedure TForm1.InputDataset3Click(Sender: TObject);
var  i:Integer;
var  startzeit,stopzeit : TDateTime;

begin
i := 0;
startzeit := now;
Screen.Cursor := crHourGlass;
ZConnection.Database := 'c:\msql\firedb.fdb';
 Zquery1.SQL.Text:='INSERT INTO Testtabelle (Wert1, Wert2) VALUES(:v1, :v2)';
 Zquery1.SQL.Text:='INSERT INTO Testtabelle (Wert1, Wert2) VALUES(:v3, :v4)';
 Zquery1.SQL.Text:='INSERT INTO Testtabelle (Wert1, Wert2) VALUES(:v5, :v6)';
 Zquery1.SQL.Text:='INSERT INTO Testtabelle (Wert1, Wert2) VALUES(:v7, :v8)';
 Zquery1.SQL.Text:='INSERT INTO Testtabelle (Wert1, Wert2) VALUES(:v9, :v10)';

 while i <= SG1.RowCount-1  do
  begin
       with ZQuery1.params do
       begin
        ParamValues['v1']:=SG1.Cells[0, i];
        ParamValues['v2']:=SG1.Cells[1, i];
        ParamValues['v3'] := SG1.Cells[0, i + 1];
        ParamValues['v4'] := SG1.Cells[1, i + 1];
        ParamValues['v5'] := SG1.Cells[0, i + 2];
        ParamValues['v6'] := SG1.Cells[1, i + 2];
        ParamValues['v7'] := SG1.Cells[0, i + 3];
        ParamValues['v8'] := SG1.Cells[1, i + 3];
        ParamValues['v9'] := SG1.Cells[0, i + 4];
        ParamValues['v10']:= SG1.Cells[1, i + 4];

      end; // with
  ZQuery1.ExecSQL;
   i := i+5;
  end; //  i
Screen.Cursor := crDefault;
stopzeit := now;
Panel1.Caption :='SuchZeit : '+ FormatDateTime('nn:ss:zzz', StopZeit - StartZeit) ;
end;
Kompilierung einwandfrei

Bei Ausführung: Kann Parameter v1 nicht finden

oder müssen die jeweiligen Parameter hinter dem jeweiligen insert Befehl stehen,
aber nein, denn dann stehen die insert Befehle ja innerhalb der while Schleife, in welcher sie ja nichts zu suchen haben (siehe Beitrag 8)

himitsu 3. Mär 2012 14:08

AW: Insert Optimierung
 
Du überschreibst ja auch immer wieder die Werte komplett, es gilt also immer nur die letzte Zuweiseung. :zwinker:

Fällt aber bestimmt auf, wen man mal nachsieht/debuggt, was für eine Query letzendlich ausgeführt wird. :angle2:
Delphi-Quellcode:
ZQuery1.SQL.Text :=
    'INSERT INTO dat12006 (Begriff, Frage) VALUES(:v1, :v2);'#10
  + 'INSERT INTO dat12006 (Begriff, Frage) VALUES(:v3, :v4);'#10
  + 'INSERT INTO dat12006 (Begriff, Frage) VALUES(:v7, :v6);'#10
  + 'INSERT INTO dat12006 (Begriff, Frage) VALUES(:v7, :v8);'#10
  + 'INSERT INTO dat12006 (Begriff, Frage) VALUES(:v9, :v10);';

ZQuery1.SQL.Clear;
ZQuery1.SQL.Add('INSERT INTO dat12006 (Begriff, Frage) VALUES(:v1, :v2);';
ZQuery1.SQL.Add('INSERT INTO dat12006 (Begriff, Frage) VALUES(:v3, :v4);';
ZQuery1.SQL.Add('INSERT INTO dat12006 (Begriff, Frage) VALUES(:v7, :v6);';
ZQuery1.SQL.Add('INSERT INTO dat12006 (Begriff, Frage) VALUES(:v7, :v8);';
ZQuery1.SQL.Add('INSERT INTO dat12006 (Begriff, Frage) VALUES(:v9, :v10);';

with ZQuery1.SQL do begin
  Clear;
  Add('INSERT INTO dat12006 (Begriff, Frage) VALUES(:v1, :v2);';
  Add('INSERT INTO dat12006 (Begriff, Frage) VALUES(:v3, :v4);';
  Add('INSERT INTO dat12006 (Begriff, Frage) VALUES(:v7, :v6);';
  Add('INSERT INTO dat12006 (Begriff, Frage) VALUES(:v7, :v8);';
  Add('INSERT INTO dat12006 (Begriff, Frage) VALUES(:v9, :v10);';
end;
(die letzen beiden Queries lassen sich auch sehrt schön automatisch generieren)

mkinzler 3. Mär 2012 14:10

AW: Insert Optimierung
 
Wobei eine parametrisierte Anweisung schneller sein sollte als eine Skript mit der wiederholten Anweisung.

Bulk-Insert könnte man auch mit Hilfe einer externen Tabelle (Textdatei) versuchen.

himitsu 3. Mär 2012 14:48

AW: Insert Optimierung
 
Man kann es kaum glauben, aber Programmieren hat tatsächlich etwas mit Mathematik zu tun.
Und wie schonmal erwähnt, fehlt noch der Rest.

Delphi-Quellcode:
procedure TForm1.InputDataset3Click(Sender: TObject);
const
  Anzahl = 30;
var
  StartZeit, StopZeit: TDateTime;
  i, i2: Integer;
begin
  StartZeit := Now;
  Screen.Cursor := crHourGlass;
  ZConnection.Database := 'c:\msql\firedb.fdb';

  ZQuery1.SQL.Clear;
  for i2 := 0 to Anzahl - 1 do
    ZQuery1.SQL.Add(Format('INSERT INTO dat12006 (Begriff, Frage) VALUES (:v%d, :v%d);', [i2 * 2, i2 * 2 + 1]));

  for i := 0 to SG1.RowCount div Anzahl - 1 do begin
    for i2 := 0 to Anzahl - 1 do begin
      ZQuery1.Params[i2 * 2 + 0].Value := SG1.Cells[0, i * Anzahl + i2];
      ZQuery1.Params[i2 * 2 + 1].Value := SG1.Cells[1, i * Anzahl + i2];
    end;
    ZQuery1.ExecSQL;
  end;

  //ZQuery1.SQL.Text := 'INSERT INTO dat12006 (Begriff, Frage) VALUES (:v0, :v1);';
  //for i := SG1.RowCount div Anzahl * Anzahl to SG1.RowCount - 1 do begin
  //  ZQuery1.Params[0].Value := SG1.Cells[0, i];
  //  ZQuery1.Params[1].Value := SG1.Cells[1, i];
  //  ZQuery1.ExecSQL;
  //end;

  if SG1.RowCount mod Anzahl > 0 then begin
    ZQuery1.SQL.Clear;
    for i2 := 0 to SG1.RowCount mod Anzahl - 1 do
      ZQuery1.SQL.Add(Format('INSERT INTO dat12006 (Begriff, Frage) VALUES (:v%d, :v%d);', [i2 * 2, i2 * 2 + 1]));

    i := SG1.RowCount div Anzahl;
    for i2 := 0 to SG1.RowCount mod Anzahl - 1 do begin
      ZQuery1.Params[i2 * 2 + 0].Value := SG1.Cells[0, i * Anzahl + i2];
      ZQuery1.Params[i2 * 2 + 1].Value := SG1.Cells[1, i * Anzahl + i2];
    end;
    ZQuery1.ExecSQL;
  end;

  Screen.Cursor := crDefault;
  StopZeit := Now;
  Panel1.Caption :='SuchZeit : ' + FormatDateTime('nn:ss:zzz', StopZeit - StartZeit) ;
end;
PS: Mit einer ordentlichen Einrückung braucht man kaum noch kommentare, um die Zusammenhänge zu erkennen. (wie z.B. wozu das END gehört)

Franzelchen 3. Mär 2012 16:51

AW: Insert Optimierung
 
@ himitsu

Ich bedanke mich erst einmal für deine Hilfe, leider liefert die Einspielung deines Quelltextes folgende Fehlermeldung:
Code:
---------------------------
Benachrichtigung über Debugger-Exception
---------------------------
Im Projekt Project2.exe ist eine Exception der Klasse EZSQLException aufgetreten. Meldung: 'SQL Error: Dynamic SQL Error SQL error code = -104 Token unknown - line 1, column 54 INSERT. Error Code: -104. Invalid token The SQL: INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?); INSERT INTO dat12006 (Begriff, Frage) VALUES (?, ?);; '. Prozeß wurde angehalten. Mit Einzelne Anweisung oder Start fortsetzen.
---------------------------
OK  Hilfe  
---------------------------
Spalte 54 weist auf
Code:
VALUES (:v%d, :v%d);'
hin. Ich habe viel zuwenig Ahnung von SQL um mit Sicherheit sagen zu können, daß es an dem fehlenden Parameter v liegt ??
Code:
ZQuery1.Params[i2 * 2 + 0].Value := SG1.Cells[0, i * Anzahl + i2];

himitsu 3. Mär 2012 16:58

AW: Insert Optimierung
 
An V vollte es nicht liegen.
Man kann entweder über den Namen den Wert eines Parameters setzen,
oder über den Index. (der erste Parameter im Statemant hat den Index 0)

Das ist also alles das Selbe, nur das nach einem Index nicht erst gesucht werden muß:
Delphi-Quellcode:
SQLQuery1.Params[0].Value
SQLQuery1.Params.Items[0].Value
SQLQuery1.Params.ParamByName('Name des ersten Parameters').Value
SQLQuery1.Params.ParamValues['Name des ersten Parameters']
ExecSQL sollte doch mehrere SQL-Statements verarbeiten können? :gruebel:


Mal aus Interesse:
Was passiert denn bei
Delphi-Quellcode:
Anzahl = 1;
?

Franzelchen 3. Mär 2012 17:17

AW: Insert Optimierung
 
Bei Anzahl 30 werden 30 Insertbefehle in einem Rutsch ausgeführt (siehe Fehlermeldung 30x insert). Bei Anzahl 1 demzufolge nur 1 Insert.

himitsu 3. Mär 2012 17:30

AW: Insert Optimierung
 
Schon klar.

Ich meinte in Bezug auf das Funktionieren, bzw. die Fehlermeldung. :stupid:

Franzelchen 3. Mär 2012 17:54

AW: Insert Optimierung
 
Zitat:

Zitat von himitsu (Beitrag 1154318)
Schon klar.

Ich meinte in Bezug auf das Funktionieren, bzw. die Fehlermeldung. :stupid:

Ich steh auf dem Schlauch...:?

himitsu 3. Mär 2012 17:58

AW: Insert Optimierung
 
Mit der 30 ist es doch in einer Exception geendet.

Passiert das auch mit einer 1, bei diesem Code?

Franzelchen 3. Mär 2012 18:15

AW: Insert Optimierung
 
bei Anzahl = 1 : keine Fehlermeldung, Die Tabelle wird erstellt.
Schon bei Anzahl = 2 : obige Fehlermeldung (nur stark verkürzt 2 Einträge). Keine Erstellung der Tabelle.

mkinzler 3. Mär 2012 19:03

AW: Insert Optimierung
 
Ein TZQuery kann aber nur einfache Statements ausführen und kein Skript. Dafür gibt es TZSqlProcessor

himitsu 3. Mär 2012 19:20

AW: Insert Optimierung
 
Ich dachte Firebird kennt die meisten SQL-Features. :gruebel:
Und irgendwie muß man doch mehrere SQL-Befehle zusammen abschicken können?

Eventuell noch INSERT SELECT UNION SELECT ...
http://www.firebirdsql.org/file/docu...ert-from-union
oder eine anonyme Funktion/Block drumrum.

[edit]
Oh, wo kommt denn mkinzler's Antwort her? :shock:

Der TZSqlProcessor schickt das zusammen ab, oder zerlegt und versendet er das einzeln?

Franzelchen 3. Mär 2012 20:00

AW: Insert Optimierung
 
Zitat:

Zitat von mkinzler (Beitrag 1154325)
Ein TZQuery kann aber nur einfache Statements ausführen und kein Skript. Dafür gibt es TZSqlProcessor

1er Gedanke: Mist
2er Gedanke: gibt's Literatur, Anwendungsbeispiele etc.

mkinzler 3. Mär 2012 20:11

AW: Insert Optimierung
 
http://zeos.firmos.at/
http://www.delphi-treff.de/tutorials...zsqlprocessor/

IBExpert 4. Mär 2012 11:15

AW: Insert Optimierung
 
kleiner Tip:

wenn es wirklich um gleichartige einfache inserts geht, dann sparst du eine Menge zeit über execute block
d.h.statt jedes insert statement mit execute/execsql senden stellt du dir deine inserts in einem Block
zusammen.

Code:
execute block
as
begin
  insert into tbl(id,txt) values (1,'A');
  insert into tbl(id,txt) values (2,'B');
  insert into tbl(id,txt) values (3,'C');
  --...
  insert into tbl(id,txt) values (26,'Z');
end
das kannst du dann mit den meisten t*query oder T*sql Komponenten als ein Befehl
mit execsql zum Server senden. So einen Block kann bis zu 32k lang sein, d.h. es
passen reichlich inserts in einen Befehl.

Über external files zu gehen ist zwar noch schneller, aber setzt voraus, das dein
Firebird Client und der Firebird Server auf einen gemeinsamen Festplattenbereich
zugreifen können, das ist nicht immer gewünscht.

Ab einer gewissen Anzahl parametern ist die Parametrisierung von statements eher langsamer
als schneller, und man sollte wissen, das bei o.a. Verfahren ca. 10 Datenpakete auf TCP/IP
Ebene zwischen Server und Client hin und her geschickt werden, bei 26 Einzelstatements
kommst du parametrisiert auf geschätzt mindestens 200 Datenpakete. Bei jedem Datenpaket
musst du immer die Latenzzeit mit einrechnen (wie lange braucht ein Ping).

Je mehr Records und je mehr Parameter, um so mehr lohnt sich das execute block verfahren!

Execute block ist seit Firebird 2 Standard und hat nichts mit execute ibeblock zu tun, was man
nur in IBExpert Tools nutzen kann.

Franzelchen 4. Mär 2012 15:45

AW: Insert Optimierung
 
Zitat:

Zitat von IBExpert (Beitrag 1154407)
kleiner Tip:

wenn es wirklich um gleichartige einfache inserts geht, dann sparst du eine Menge zeit über execute block
d.h.statt jedes insert statement mit execute/execsql senden stellt du dir deine inserts in einem Block
zusammen.

Code:
execute block
as
begin
  insert into tbl(id,txt) values (1,'A');
  insert into tbl(id,txt) values (2,'B');
  insert into tbl(id,txt) values (3,'C');
  --...
  insert into tbl(id,txt) values (26,'Z');
end

Alle 35000 Datensätze, deren Zahl auf Dauer wächst, in ein oder zwei derartiger Blöcke, also 35000 einzelne Inserts... oder verstehe ich das falsch?
Wie wird dann ein solcher Block in Delphi eingebunden?

himitsu 4. Mär 2012 15:59

AW: Insert Optimierung
 
Ist doch praktisch sowas ähliches, wie mehrere Inserts zusammen, nur in einem anonymen Block verpackt.

Von der Größe des SQL-Statements her könntest du ja nochmal Folgendes probieren, mit der nun neu bekannten SQL-Komponente.
Code:
insert into tbl(id,txt) values (1,'A'), (1,'A'), (1,'A'), (1,'A');
Und wenn die Übertragung der Parameter soviel frißt, bei der Übertagung, dann könnte man ja auch nochmal versuchen keine Parameter zu nutzen.
Aber ob sich das wirklich positiv auswirkt, müßte man mal sehn ... schließlich muß man dann die Parameter selber "ordnungsgemäß" behandeln und das SQL-Statement muß jedesmal neu zusammengebaut und vorallem erneut geparst werden.

Franzelchen 4. Mär 2012 18:05

AW: Insert Optimierung
 
Also ich habe große Schwierigkeiten mir 35000 und mehr Insert befehle in einem Block vorzustellen.
Kann mir zum Thema execute Block jemand Einführungsliteratur nennen?

mkinzler 4. Mär 2012 18:13

AW: Insert Optimierung
 
Ein execute block ist eine SP, die inline deklariert wird. Es ist also SQL/PL möglich. Bei einfachen Inserts sollte aber eine normale parametrisierte Insert-Abfrage ausreichen. Auch hier kannst du die Commits blockweise ausfügren.

Franzelchen 4. Mär 2012 19:05

AW: Insert Optimierung
 
Zitat:

Zitat von mkinzler (Beitrag 1154487)
Ein execute block ist eine SP, die inline deklariert wird. Es ist also SQL/PL möglich. Bei einfachen Inserts sollte aber eine normale parametrisierte Insert-Abfrage ausreichen. Auch hier kannst du die Commits blockweise ausfügren.

SP ist eine stored procedure,
inline ist ???
SQL/PL heißt ???

Warum müssen die Antworten komplizierter sein als die Fragen, welche man versucht zu stellen.

Ich weiß ich bin Anfänger und sollte mich um die Grundlagen selber kümmern. Dennoch, wozu ist das Forum da, wenn es keine Anfänger gäbe, sondern nur Fachleute?

hoika 4. Mär 2012 19:15

AW: Insert Optimierung
 
Hallo,

als Anfänger solltest du ja erst einmal anfangen,
statt sich gleich an die Optimierung zu setzen.

Was ist an 4 min Laufzeit sooo schlimm,
dieses Insert wird doch wohl nicht zu den Haupt-Aufgaben deines Programms gehören.

Mach doch erst mal so weiter.


Noch was zum Probieren

Delphi-Quellcode:
procedure TForm1.InputDatasetClick(Sender: TObject);
var i:Integer;
 startzeit,stopzeit : TDateTime;

begin
startzeit := now;
Screen.Cursor := crHourGlass;
ZConnection.Database := 'c:\msql\firedb.fdb';

 ZQuery1.SQL.text:='INSERT INTO TestTabelle (Wert1, Wert2) VALUES(:v1, :v2)';
 ZQuery1.Prepare;

 for i:=0 to SG1.RowCount-1 do
  begin
    with ZQuery1.params do
    begin
      ParamValues['v1']:=SG1.Cells[0, i];
      ParamValues['v2']:=SG1.Cells[1, i];
    end; // with
   ZQuery1.ExecSQL;
  end; // i
Screen.Cursor := crDefault;
stopzeit := now;
Panel1.Caption :='SuchZeit : '+ FormatDateTime('nn:ss:zzz', StopZeit - StartZeit) ;
end;
Was du leider immer noch nicht geschrieben hattest:
Hat deine Tabelle Indizes ?

Heiko

mkinzler 4. Mär 2012 19:21

AW: Insert Optimierung
 
Zitat:

SP ist eine stored procedure,
Ja.
Zitat:

inline ist ???
Der Code wird nicht gespeichert, wie bei einer SP oder einem Trigger, sondern wird innerhalb einer Abfarge deklariert und existiert dann nur temporär.
Zitat:

SQL/PL heißt ???
PL (procedure language) bieter erweitere Konstrukte wie temp./lokale Variablen, Strukturelmente ( Schleifen, Bedingungen usw.), welche in einer normalen Abfarge nicht möglich sind.
http://www.janus-software.com/fbmanual/

Franzelchen 4. Mär 2012 20:33

AW: Insert Optimierung
 
@ mkinzler

Danke.

@ Hoika

Die Tabelle hat weder Indizes noch Trigger.

Zu dem Quelltext:
ZeosQuery (habe ich gelesen) besitzt die einstellbare Prepare Funktion nicht, da diese Funktion im Quelltext von Zeos bereits fest einprogrammiert ist.

Mein erster Schritt: create DB, zweiter Schritt: create tabelle, dritter Schritt: bring meine Daten in die Tabelle(möglichst schnell).


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