Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Delphi Schnellste Insert Möglichkeit für eine DB? (https://www.delphipraxis.net/148461-schnellste-insert-moeglichkeit-fuer-eine-db.html)

moelski 2. Mär 2010 12:00

Datenbank: FireBird • Version: 2.5 • Zugriff über: UniDac

Schnellste Insert Möglichkeit für eine DB?
 
Moin !

Was ist eigentlich die schnellste Variante, Daten in einer DB einzutragen?

Nehmen wir mal an ich möchte die Daten eines Messgeräts in eine Tabelle sichern. Dann gibt es da (soweit ich das bis hier überblicke) 2 Methoden:
1) mittels FieldByName / Append
Delphi-Quellcode:
DBQuery.Edit;
DBQuery.FieldByName('ID').AsInteger            := Counter;
DBQuery.FieldByName('PROP01').AsFloat          := Random(10000) / 10;
DBQuery.FieldByName('PROP02').AsFloat          := Random(10000) / 10;
DBQuery.FieldByName('PROP03').AsFloat          := Random(10000) / 10;
DBQuery.FieldByName('DateTime').AsSQLTimeStamp := DateTimeToSQLTimeStamp(Now);
DBQuery.Append;
2) mittels SQL Insert (und das dann eben mit x Werten in einem SQL Statement)
Delphi-Quellcode:
DM.Query.SQL.Text := 'INSERT Into Demo (demo.wert1) VALUES (100);';
DM.Query.ExecSQL;
Aber was geht nun am schnellsten? Und gibt es noch andere Varianten?

Sharky 2. Mär 2010 12:08

Re: Schnellste Insert Möglichkeit für eine DB?
 
Zitat:

Zitat von moelski
... Aber was geht nun am schnellsten? Und gibt es noch andere Varianten?

Ich denke das Variante 2 unter verwendung von Parametern am schnellsten sein sollte.

chaosben 2. Mär 2010 12:43

Re: Schnellste Insert Möglichkeit für eine DB?
 
Nur mal so'n Gedankenfurz: Das schnellste laut HK ist eine Externe Tabelle die man mit
SQL-Code:
insert into tabelle (bla) select bla from externe_tabelle
anzapft.

Die Frage die jetzt noch zu klären wäre: Kann man eine external Table von "außen" beschreiben, während man sie von der DB genutzt wird?

sirius 2. Mär 2010 12:45

Re: Schnellste Insert Möglichkeit für eine DB?
 
SQL-Code:
DM.Query.SQL.Text := 'INSERT Into Demo (demo.wert1) VALUES (100), Values(200), Values(400);';
DM.Query.ExecSQL;

Sharky 2. Mär 2010 13:06

Re: Schnellste Insert Möglichkeit für eine DB?
 
Hi Sirius,

wäre das nicht schneller (bei vielen aufeinander folgenden Eintragungen)?

SQL-Code:
DM.Query.SQL.Text := 'INSERT Into Demo (demo.wert1, demo.wert2, demo.wert3) VALUES (:wert, :wert2, :wert3);';
DM.Query.Prepare;
DM.Query.Params[0].AsInteger := 100;
DM.Query.Params[1].AsInteger := 200;
DM.Query.Params[2].AsInteger := 400;
DM.Query.ExecSQL;
Jetzt muss das DBMS nicht jedesmal den SQL-Query parsen. Für die weiteren Einträge dann natürlich nuch noch Zeile 3-6 ausführen.

moelski 2. Mär 2010 13:28

Re: Schnellste Insert Möglichkeit für eine DB?
 
Moin !

Ich habe mal alle 3 Varianten umgesetzt. (Code siehe unten).

Count : 1000
1 Time : 10390 ms
2 Time : 8016 ms
3 Time : 14952 ms

Count : 100
1 Time : 1062 ms
2 Time : 985 ms
3 Time : 2296 ms

Die Ergebnisse zeigen das es derzeit so ausschaut als wenn die reine Insert Methode die schnellste wäre.
Danach kommt die FieldByName Variante. Und am Ende steht die Parameter Variante von Sharky (was mich sehr erstaunt).

Count bezieht sich auf die anzahl der Datensätze die eingetragen werden.

Hier noch der verwendete Code:
Delphi-Quellcode:
procedure TDBThread.Execute;
var I        : Integer;
    StartTime : TDateTime;
    SQl : String;
begin
  StartTime := Now;
  DBQuery.Edit;
  for I := 1 to Amount do begin
    Inc(Counter);
    DBQuery.FieldByName('ID').AsInteger            := Counter;
    DBQuery.FieldByName('PROP01').AsFloat          := Random(10000);
    DBQuery.FieldByName('PROP02').AsFloat          := Random(10000);
    DBQuery.FieldByName('PROP03').AsFloat          := Random(10000);
    DBQuery.FieldByName('PROP04').AsFloat          := Random(10000);
    DBQuery.FieldByName('PROP05').AsFloat          := Random(10000);
    DBQuery.FieldByName('PROP06').AsFloat          := Random(10000);
    DBQuery.FieldByName('PROP07').AsFloat          := Random(10000);
    DBQuery.FieldByName('PROP08').AsFloat          := Random(10000);
    DBQuery.FieldByName('PROP09').AsFloat          := Random(10000);
    DBQuery.FieldByName('PROP10').AsFloat          := Random(10000);
    DBQuery.FieldByName('DateTime').AsSQLTimeStamp := DateTimeToSQLTimeStamp(Now);

    DBQuery.Append;
  end;
  TimeMS := MilliSecondsBetween(Now, StartTime);
  Form2.Memo1.Lines.Add('1 Time : ' + IntToStr(TimeMS) + ' ms');

  StartTime := Now;
  for I := 1 to Amount do begin
    Inc(Counter);
    SQl :=                   'INSERT Into ' + Table;
    SQl := SQl + ' (ID, PROP01, PROP02, PROP03, PROP04, PROP05, PROP06, PROP07, PROP08, PROP09, PROP10)';
    SQl := SQl + ' VALUES (' + IntToStr(Counter) + ', ';
    SQl := SQl + FloatToStr(Random(10000) ) + ', ';
    SQl := SQl + FloatToStr(Random(10000) ) + ', ';
    SQl := SQl + FloatToStr(Random(10000) ) + ', ';
    SQl := SQl + FloatToStr(Random(10000) ) + ', ';
    SQl := SQl + FloatToStr(Random(10000) ) + ', ';
    SQl := SQl + FloatToStr(Random(10000) ) + ', ';
    SQl := SQl + FloatToStr(Random(10000) ) + ', ';
    SQl := SQl + FloatToStr(Random(10000) ) + ', ';
    SQl := SQl + FloatToStr(Random(10000) ) + ', ';
    SQl := SQl + FloatToStr(Random(10000) ) + ');';
    //SQl := SQl + SQLTimeStampToStr('ddd d of mmm yyyy', DateTimeToSQLTimeStamp(Now)) + ');';
    DBQuery.SQL.Text := SQL;
    DBQuery.ExecSQL;
  end;
  TimeMS := MilliSecondsBetween(Now, StartTime);
  Form2.Memo1.Lines.Add('2 Time : ' + IntToStr(TimeMS) + ' ms');

  StartTime := Now;
  for I := 1 to Amount do begin
    Inc(Counter);
    DBQuery.SQL.Text := 'INSERT Into ' + Table + ' (ID, PROP01, PROP02, PROP03, PROP04, PROP05, PROP06, PROP07, PROP08, PROP09, PROP10) VALUES (:wert1, :wert2, :wert3, :wert4, :wert5, :wert6, :wert7, :wert8, :wert9, :wert10, :wert11);';
    DBQuery.Prepare;
    DBQuery.Params[0].AsInteger := Counter;
    DBQuery.Params[1].AsFloat := Random(10000);
    DBQuery.Params[2].AsFloat := Random(10000);
    DBQuery.Params[3].AsFloat := Random(10000);
    DBQuery.Params[4].AsFloat := Random(10000);
    DBQuery.Params[5].AsFloat := Random(10000);
    DBQuery.Params[6].AsFloat := Random(10000);
    DBQuery.Params[7].AsFloat := Random(10000);
    DBQuery.Params[8].AsFloat := Random(10000);
    DBQuery.Params[9].AsFloat := Random(10000);
    DBQuery.Params[10].AsFloat := Random(10000);
    DBQuery.ExecSQL;
  end;

  TimeMS := MilliSecondsBetween(Now, StartTime);
  Form2.Memo1.Lines.Add('3 Time : ' + IntToStr(TimeMS) + ' ms')
end;

Sharky 2. Mär 2010 13:31

Re: Schnellste Insert Möglichkeit für eine DB?
 
Hai moelski,

ich habe den letzten Absatz in meinem Posting erweitert. Ansonsten machen die Params keinen Sinn.

Test es doch mal so:
Delphi-Quellcode:
    DBQuery.SQL.Text := 'INSERT Into ' + Table + ' (ID, PROP01, PROP02, PROP03, PROP04, PROP05, PROP06, PROP07, PROP08, PROP09, PROP10) VALUES (:wert1, :wert2, :wert3, :wert4, :wert5, :wert6, :wert7, :wert8, :wert9, :wert10, :wert11);';
    DBQuery.Prepare;
   for I := 1 to Amount do begin
    Inc(Counter);
    DBQuery.Params[0].AsInteger := Counter;
    DBQuery.Params[1].AsFloat := Random(10000);
    DBQuery.Params[2].AsFloat := Random(10000);
    DBQuery.Params[3].AsFloat := Random(10000);
    DBQuery.Params[4].AsFloat := Random(10000);
    DBQuery.Params[5].AsFloat := Random(10000);
    DBQuery.Params[6].AsFloat := Random(10000);
    DBQuery.Params[7].AsFloat := Random(10000);
    DBQuery.Params[8].AsFloat := Random(10000);
    DBQuery.Params[9].AsFloat := Random(10000);
    DBQuery.Params[10].AsFloat := Random(10000);
    DBQuery.ExecSQL;
  end;

sirius 2. Mär 2010 13:31

Re: Schnellste Insert Möglichkeit für eine DB?
 
@Sharky
Ja natürlich mit Parametern.
Ich meinte mit meinem Post, dass man auch mehrere Zeilen in einem Insert-Statement unterbringen kann (zumindest in Oracle).

moelski 2. Mär 2010 13:35

Re: Schnellste Insert Möglichkeit für eine DB?
 
@Sharky:
Ich sehe jetzt gerade die Differenz zu meinem Code nicht :gruebel:

Sharky 2. Mär 2010 13:41

Re: Schnellste Insert Möglichkeit für eine DB?
 
Zitat:

Zitat von moelski
@Sharky:
Ich sehe jetzt gerade die Differenz zu meinem Code nicht :gruebel:

Du setzt das SQL-Kommando innerhalb der Schleife. Darum muss es jedes mal neu geparst werden.
Ich setzte es nur einmal und übergebe in der Schleife dann nur die geänderten Parameter.

moelski 2. Mär 2010 13:56

Re: Schnellste Insert Möglichkeit für eine DB?
 
Moin !

Ah ok. So habe das mal angepasst mit erstaunlichem Ergebnis :-)
Count : 1000
1 Time : 12093 ms
2 Time : 9156 ms
3 Time : 6015 ms

Also nochmals deutlich schneller bei 1000 Datensätzen.

Das war jetzt alles auf Basis des Firebird Servers (2.5).
werde jetzt mal embedded testen und schaun ob das noch schneller geht?!

moelski 2. Mär 2010 14:06

Re: Schnellste Insert Möglichkeit für eine DB?
 
Moin !

So wenn ich das ganze embedded laufen lasse ändert sich nicht viel.

Count : 1000
1 Time : 6968 ms
2 Time : 6781 ms
3 Time : 4656 ms

Es ist zwar etwas schneller als auf dem Server, aber nicht wirklich gravierend.


Jetzt könnte man noch testen ob es etwas bringt wenn man mehrere INSERT in einen SQL Text packt.
Das würde aber auf der anderen Seite auch bedeuten das ich in der Anwendung die Daten puffern muss.

Mal sehen ob ich das noch teste ...

Wenn jemand noch einen anderen Vorschlag hat ... Nur her damit. :)

shmia 2. Mär 2010 16:48

Re: Schnellste Insert Möglichkeit für eine DB?
 
Die SQL-Befehle sollte man nicht über eine Query-Komponente absenden, sondern falls möglich direkt über das Connection-Objekt oder Database-Objekt.
Beim Zusammenbauen der SQL-Befehle sollte man darauf achten, dass man nicht Folgendes tut:
Delphi-Quellcode:
SQL := SQL + ....;
Hierbei würde jedes Mal der String verlängert, was zur Folge hat, dass ein neuer Speicherblock notwendig wird und dann der alte Block auf den neuen kopiert wird.

moelski 2. Mär 2010 17:46

Re: Schnellste Insert Möglichkeit für eine DB?
 
Ok ich werde das mal testen.

Wusste nicht dass das mit UniDac so geht.
Danke für den Hinweis.

hoika 2. Mär 2010 17:49

Re: Schnellste Insert Möglichkeit für eine DB?
 
Hallo,

was ich hier vermisse,
sind die Transaktionen.

sollte UniDAC wie die BDE arbeiten,
würde bei jedem ExecSQL ein StartTransaction / Commit gemacht.

Das sollte aber ausserhalb der Schleife passieren.

Delphi-Quellcode:
StartTransaction;
try
  Prepare;
  for
    ExecSQL;
finally
  Commit;
end;

Heiko

moelski 2. Mär 2010 17:57

Re: Schnellste Insert Möglichkeit für eine DB?
 
Hallo Heiko,

evtl. ist das Beispiel dafür ungeeignet, aber die Daten kommen bei mir ja im Sekundentakt später. Die For Schleife ist derzeit ja nur zum simulieren großer Datenmengen.

Muss ich dann zwangsläufig auch mit Transaktions arbeiten?

s.h.a.r.k 2. Mär 2010 22:37

Re: Schnellste Insert Möglichkeit für eine DB?
 
Zitat:

Zitat von shmia
Die SQL-Befehle sollte man nicht über eine Query-Komponente absenden, sondern falls möglich direkt über das Connection-Objekt oder Database-Objekt.

Warum *genau* sollte man das nicht tun?

Zitat:

Zitat von shmia
Beim Zusammenbauen der SQL-Befehle sollte man darauf achten, dass man nicht Folgendes tut:
Delphi-Quellcode:
SQL := SQL + ....;

Sowas darf ich dann aber machen, ohne dass es Probleme mit dem Speicher gibt, oder?

Delphi-Quellcode:
sql := 'SELECT * FROM ' + table + ' WHERE ID = ' + id + ' ORDER BY ' + order;

Jürgen Thomas 3. Mär 2010 08:25

Re: Schnellste Insert Möglichkeit für eine DB?
 
Zitat:

Zitat von s.h.a.r.k
Sowas darf ich dann aber machen, ohne dass es Probleme mit dem Speicher gibt, oder?
Delphi-Quellcode:
sql := 'SELECT * FROM ' + table + ' WHERE ID = ' + id + ' ORDER BY ' + order;

Sowas sollst du grundsätzlich nicht machen! Das hat nichts mit dem Speicher(n) zu tun, sondern ist eine Frage der Sicherheit und Geschwindigkeit (und wird in 1000 Beiträgen dringend empfohlen):

* Parameter vermeiden Probleme bei der Formatierung von Zahlen, Datumsangaben oder Hochkommata als Teil von Strings.
* Parameter verhindern Sql-Injection.
* Ein parametrisierter Befehl, der der DB schon bekannt ist, muss nicht neu analysiert werden und wird deshalb schneller ausgeführt. Ein Befehl, der z.B. durch die "eingebaute ID" jedesmal anders aussieht, muss jedesmal neu analysiert werden.

Ich hoffe, das ist so klar und eindeutig, dass es nicht mehr in Zweifel gezogen wird. Jürgen

psychomama 3. Mär 2010 08:57

Re: Schnellste Insert Möglichkeit für eine DB?
 
Darf ich dann bitte einmal fragen, wie man sonst eine dynamische SQL-Anweisung schreiben soll?!

p80286 3. Mär 2010 09:39

Re: Schnellste Insert Möglichkeit für eine DB?
 
Zitat:

Zitat von Jürgen Thomas
* Parameter vermeiden Probleme bei der Formatierung von Zahlen, Datumsangaben oder Hochkommata als Teil von Strings.
* Parameter verhindern Sql-Injection.
* Ein parametrisierter Befehl, der der DB schon bekannt ist, muss nicht neu analysiert werden und wird deshalb schneller ausgeführt. Ein Befehl, der z.B. durch die "eingebaute ID" jedesmal anders aussieht, muss jedesmal neu analysiert werden.

Ich hoffe, das ist so klar und eindeutig, dass es nicht mehr in Zweifel gezogen wird. Jürgen

Ach wenn ich Dir Recht geben muß, sind das keine allgemeingültigen Aussagen!
Wenn z.b. der Benutzer keine Chance hat an den SQL-Text zu kommen (Injection), dann kann
SQL-Code:
sql:=sql+....
durchaus sinnvoll sein.
In den allermeisten Fällen ist Deine Aussage korrekt, aber es gibt eben auch Ausnahmen, darum sollte man nicht ganz so dogmatisch sein.

Gruß
K-H

Jürgen Thomas 3. Mär 2010 09:48

Re: Schnellste Insert Möglichkeit für eine DB?
 
Zitat:

Zitat von psychomama
Darf ich dann bitte einmal fragen, wie man sonst eine dynamische SQL-Anweisung schreiben soll?!

Beispielsweise so, wie es oben in #5 und #7 gemacht wurde. Die Tabellen, Spalten und JOINs dürfen selbstverständlich dynamisch zusammengesetzt werden; auch die Parameter dürfen auf diese Weise dynamisch eingebaut werden, aber keinesfalls die Werte für die Parameter.

Zitat:

Zitat von p80286
In den allermeisten Fällen ist Deine Aussage korrekt, aber es gibt eben auch Ausnahmen, darum sollte man nicht ganz so dogmatisch sein.

Es mag solche Ausnahmen geben. Aber die Regel ist so eindeutig; ich wundere mich sowieso, dass das in dieser Diskussion nicht viel deutlicher gesagt wurde, sondern nur implizit danach gehandelt wurde.

Jürgen

s.h.a.r.k 3. Mär 2010 11:30

Re: Schnellste Insert Möglichkeit für eine DB?
 
@Jürgen Thomas: Das mit den Paramtern kannte ich schon :) Meine Frage hatte eher etwas mit der Speicherwaltung zu tun, wenn ich einen String über "+" zusammenbaue. Man merkt vor allem schnell, dass es zu einem Problem werden kann, wenn man verschiedene Dateitypen in eine DB klopfen kann und das über einen einzigen SQL-String machen will. Man muss die Logik der Params halt selbst basteln -> unnötige Arbeit.

Zitat:

Zitat von s.h.a.r.k
Zitat:

Zitat von shmia
Die SQL-Befehle sollte man nicht über eine Query-Komponente absenden, sondern falls möglich direkt über das Connection-Objekt oder Database-Objekt.

Warum *genau* sollte man das nicht tun?

Diese Frage wäre noch offen :zwinker:

IBExpert 3. Mär 2010 17:50

Re: Schnellste Insert Möglichkeit für eine DB?
 
wenn du es von der Applikationsseite wirklich schnell haben willst, dann achte auf die Transaktionssteuerung und auf das prepare außerhalb der schleife und tausche innerhalb der Schleife nur noch die parameter aus. Bei jedem execsql kommt dann sonst ein riesen overhead zu stande. Den Tip von hoika also auf jeden Fall ernst nehmen. Wenn du jedoch in der Schleife das SQL änderst, dann macht der client beim server sowieso ein unprepare und ein neues prepare mit jedem execsql. Damit würdest du wieder Geschwindigkeit verlieren.

Wenn die Daten zum Beispiel als Datei vorliegen, dann konvertier ich die lieber mit Delphi in ein Fixed format (pro zeile wird jedes feld mit Leerzeichen aufgefüllt).
von csv
SQL-Code:
ID;ZAHL;TXT;TXT2;ZAHL2
1;2;abc;xy;5
2;1;asd;yss;7
4;1;bbs;ddd;5

in fixed format, z.B.

SQL-Code:
1  2 abc xy 5
2  1 asd yss 7
4  1 bbs ddd 5
die datei dann zum Beispiel als c:\dat.txt speichern

dann in firebird

SQL-Code:
create table dat_ext external file 'C:\dat.txt'
(id char(3),
zahl char(2),
txt char(4),
txt2 char(3),
zahl2 char(1),
crlf char(2))    --zeilenumbruch in Textdatei belegt bei windoof 2 byte
als interne tabelle zum Beispiel

SQL-Code:
create table dat
(id integer,
zahl integer,
txt char(4),
txt2 char(3),
zahl2 integer)

dann in firebird

SQL-Code:
insert into dat select id,zahl,txt,zahl2,txt2 from dat_ext

und dann ganz einfach

SQL-Code:
drop table dat_ext;
damit bekommst du locker 20000 bis 100000 records pro sekunde in die Datenbank. Das geht in einer Transaktion mit wenigen execsqls, dabei ist dann der prepare auch egal.

Das entspricht dem Tip von chaosben und schlägt alle anderen verfahren um längen.

So ein Präprozessor, also ein simples Programm was aus deinen vorhandenen Daten eben so ein fixed format erzeugt ist in Delphi sogar mit TStringlist schnell zusammengeklöppelt und braucht sicherlich auch für 100000 records weniger als eine Sekunden, wenn das nicht ganz bescheuert programmiert ist.


Schöne Grüße

shmia 4. Mär 2010 14:02

Re: Schnellste Insert Möglichkeit für eine DB?
 
Zitat:

Zitat von s.h.a.r.k
Zitat:

Zitat von shmia
Die SQL-Befehle sollte man nicht über eine Query-Komponente absenden, sondern falls möglich direkt über das Connection-Objekt oder Database-Objekt.

Warum *genau* sollte man das nicht tun?

Aus Performancegründen.
Ich beziehe mich hier mal auf ADO-Komponenten.
Wenn man einen INSERT direkt über Connnection.Execute('INSERT INTO ....') einreicht, dann ist man deutlich schneller als wenn man dies über eine ADOQuery oder ADOCommand tut.

Auch bei anderen Datenbank-Komponenten, die das Konzept einer Connection haben muss das so sein.
Letztendlich gibt eine Query ihren SQL-Befehl immer an die Connection- oder Database-Komponenten weiter.

p80286 4. Mär 2010 15:30

Re: Schnellste Insert Möglichkeit für eine DB?
 
Zitat:

Zitat von shmia
Aus Performancegründen.
Ich beziehe mich hier mal auf ADO-Komponenten.
Wenn man einen INSERT direkt über Connnection.Execute('INSERT INTO ....') einreicht, dann ist man deutlich schneller als wenn man dies über eine ADOQuery oder ADOCommand tut.

Auch bei anderen Datenbank-Komponenten, die das Konzept einer Connection haben muss das so sein.
Letztendlich gibt eine Query ihren SQL-Befehl immer an die Connection- oder Database-Komponenten weiter.

wofür ist dann die TQuery noch gut?

Gruß
K-H

Bernhard Geyer 4. Mär 2010 15:43

Re: Schnellste Insert Möglichkeit für eine DB?
 
Zitat:

Zitat von p80286
Ach wenn ich Dir Recht geben muß, sind das keine allgemeingültigen Aussagen!
Wenn z.b. der Benutzer keine Chance hat an den SQL-Text zu kommen (Injection), dann kann
SQL-Code:
sql:=sql+....
durchaus sinnvoll sein.
In den allermeisten Fällen ist Deine Aussage korrekt, aber es gibt eben auch Ausnahmen, darum sollte man nicht ganz so dogmatisch sein.

Dann viel Spaß beim Erklären wenn in einem Sicherheitsaudit der Auditor erstmal den SQL-Tracer anschmeißt und sieht das es Stellen gibt die nicht mit parametrisierte Abfragen laufen. Wie willst du Sicherstellen das die Methode die sowas macht nicht doch mal mit Werten auf User-Eingaben gefüttert wird? Z.B. 2ter Entwickler der die internas einer Methode nicht kennt bzw. sich nicht anschaut.

shmia 4. Mär 2010 16:16

Re: Schnellste Insert Möglichkeit für eine DB?
 
Zitat:

Zitat von p80286
wofür ist dann die TQuery noch gut?

TQuery braucht man immer dann, wenn man eine Datenmenge zurückerwartet (das ist im Prinzip alles was mit SELECT ... anfängt).
TQuery bietet jede Menge Komfort (Parameter, Events,..) der halt etwas Zeit kostet.

p80286 4. Mär 2010 16:38

Re: Schnellste Insert Möglichkeit für eine DB?
 
Hallo Berhard

Zitat:

Zitat von Bernhard Geyer
Dann viel Spaß beim Erklären wenn in einem Sicherheitsaudit der Auditor erstmal den SQL-Tracer anschmeißt und sieht das es Stellen gibt die nicht mit parametrisierte Abfragen laufen. Wie willst du Sicherstellen das die Methode die sowas macht nicht doch mal mit Werten auf User-Eingaben gefüttert wird? Z.B. 2ter Entwickler der die internas einer Methode nicht kennt bzw. sich nicht anschaut.

in Meiner Praxis gibt es z.B. folgende Situation:

Code:
select tabtyp from Tabelle1;

case tabtyp of
  Akte1 : sqltext:= 'select info from Tabelle2';
  Akte2 : sqltext:= 'select info from Tabelle3';
  Akte3 : sqltext:= 'select info1 from Tabelle4,Tabelle5 where Tabelle4.xid=Tabelle5.txid';
usw.
wie willst Du das mit Parametern erledigen?

solche Konstrukte wie
Code:
sqltext:='select adresse from tabelle1 where vorname='+form1.edit1.text+';'
sind natürlich tödlich.

und auch soetwas kommt auch nicht in die Tüte:

Code:
sqltext:='select adresse from tabelle1 where vorname='+Listbox1.items[listbox1.selected]+';'
Gruß
K-H

alzaimar 4. Mär 2010 18:35

Re: Schnellste Insert Möglichkeit für eine DB?
 
Zitat:

Zitat von p80286
wie willst Du das mit Parametern erledigen?

Zur Laufzeit parametrisierte Queries zuweisen, die Datentypen der Parameter definieren, parametrieren und ab die Luzie.

p80286 5. Mär 2010 09:26

Re: Schnellste Insert Möglichkeit für eine DB?
 
Zitat:

Zitat von alzaimar
Zitat:

Zitat von p80286
wie willst Du das mit Parametern erledigen?

Zur Laufzeit parametrisierte Queries zuweisen, die Datentypen der Parameter definieren, parametrieren und ab die Luzie.

Äh wie soll das dann aussehen?
Gruß
K-H

Stevie 5. Mär 2010 10:56

Re: Schnellste Insert Möglichkeit für eine DB?
 
Das Problem bei deinem Beispiel oben ist, dass es dort keine Möglichkeit gibt, zu parametrisieren, da du die komplette Tabelle selektierst.
Das Statement, was du selbst als tödlich bezeichnest, ist ein Kandidat dafür.

p80286 5. Mär 2010 11:14

Re: Schnellste Insert Möglichkeit für eine DB?
 
Mir scheint wir schreiben aneinander vorbei.

Code:
sqltext:='select adresse from tabelle1 where vorname='+form1.edit1.text+';'
sollte (darf)darf nicht genutzt werden

richtig wäre dies hier:
Code:
sqltext:='select adresse from tabelle1 where vorname=:prmvorname ;'
Gleiches gilt für die Übergabe aus Listboxen u ä.
also statt
Code:
sqltext:='select adresse from tabelle1 where vorname='+Listbox1.items[listbox1.selected]+';'
dies
Code:
sqltext:='select adresse from tabelle1 where vorname=:prmvorname ;'
Aber wie geht man mit folgender Situation um (stark vereinfacht):
Code:
select tabtyp from Tabelle1;

case tabtyp of
  Akte1 : sqltext:= 'select info from Tabelle2';
  Akte2 : sqltext:= 'select info from Tabelle3';
  Akte3 : sqltext:= 'select info1 from Tabelle4,Tabelle5 where Tabelle4.xid=Tabelle5.txid';
usw.
Aus einer Abfrage ergibt sich ein Wert, der die folgende Abfrage bestimmt. (z.B. die Tabelle)

Gruß
K-H

moelski 8. Mär 2010 08:05

Re: Schnellste Insert Möglichkeit für eine DB?
 
Moin !

Hulla, habe das We hier nicht rein gesehen und nun entstehen hier fast Grundsatzdiskussionen :)

Ich möchte aber nochmal auf den Kern meines Problems zurück kommen.
Mein Beispiel ganz zu Anfang:
Delphi-Quellcode:
for I := 1 to Amount do begin
    Inc(Counter);
    DBQuery.SQL.Text := 'INSERT Into ' + Table + ' (ID, PROP01, PROP02, PROP03, PROP04, PROP05, PROP06, PROP07, PROP08, PROP09, PROP10) VALUES (:wert1, :wert2, :wert3, :wert4, :wert5, :wert6, :wert7, :wert8, :wert9, :wert10, :wert11);';
    DBQuery.Prepare;
    DBQuery.Params[0].AsInteger := Counter;
    DBQuery.Params[1].AsFloat := Random(10000);
    ...
    DBQuery.Params[10].AsFloat := Random(10000);
    DBQuery.ExecSQL;
  end;
... war auch nur ein Beispiel und trifft nicht den Kern meines Problems.

Die For Schleife dient hier ja nur zum Erzeugen von Last. In Wirklichkeit aber kommen die Daten bei uns von Geräten. Die Geräte senden (um es einfach zu machen) Spannung, Strom, Ladung. Und das mehr oder minder schnell.

Wir haben also nicht einen Block an Daten den ich auf einen Schlag wegschreiben möchte (was man dann ja über eine Transaktion könnte), sondern wir haben eher eine stetige Datenmenge die permanent weggeschrieben werden muss.

Ich sehe da im Moment zwei Varianten:
1) Man schreibt jeden Datensatz sofort bei Erscheinen in die DB. Das (so verstehe ich das hier) erzeugt aber eine Menge Overhead.
2) Ich könnte Datensätze erst puffern und dann z.B. immer 10 Datensätze mittels Transaktion wegschreiben. Das würde den Overhead auf der DB verringern, aber mehr Aufwand am Client bedeuten.

Wie seht ihr das?

exilant 8. Mär 2010 10:31

Re: Schnellste Insert Möglichkeit für eine DB?
 
Zitat:

Zitat von moelski
Moin !

Ich sehe da im Moment zwei Varianten:
1) Man schreibt jeden Datensatz sofort bei Erscheinen in die DB. Das (so verstehe ich das hier) erzeugt aber eine Menge Overhead.
2) Ich könnte Datensätze erst puffern und dann z.B. immer 10 Datensätze mittels Transaktion wegschreiben. Das würde den Overhead auf der DB verringern, aber mehr Aufwand am Client bedeuten.

Wie seht ihr das?

Wir erfassen hier ebenfalls in einer 24/7 Umgebung große Mengen von Messwerten. Es hat sich bewährt, die Daten lokal zu puffern und dann
blockweise in die DB zu schreiben. Wie die Pufferung aussieht richtet sich danach, welche Zeitverzögerung Du Dir erlauben kannst.
Ich puffere hier Blöcke von 3 Min. bis zu 1 Stunde.
Die Daten werden lokal in CSV Dateien geschrieben und dann nach Zeitraum X and den Server übertragen (FTP mit den Indys). Der schreibt dann diese Daten in _EINER_ Transaktion in die DB (Firebird). Es handelt sich dabei dann um einige hundert bis zu ettlichen tausend "inserts". Das geht granatenschnell und läuft hier sehr zuverlässig.
Direkt in die Datenbank werden nur die Daten geschrieben, deren Überwachung kritisch ist. Das erledigen die entsprechenden Clients dann selber.

moelski 8. Mär 2010 12:08

Re: Schnellste Insert Möglichkeit für eine DB?
 
Moin !

Zitat:

Die Daten werden lokal in CSV Dateien geschrieben und dann nach Zeitraum X and den Server übertragen
Das fällt bei uns schon mal raus, denn wir nutzen an der Stelle die Embedded Version und keinen Server.

Aber gut, dann würde man die Daten sammeln und in einem eigenen Thread nach x Sekunden / Minuten / ... in die DB schieben.

p80286 8. Mär 2010 12:56

Re: Schnellste Insert Möglichkeit für eine DB?
 
Zitat:

Zitat von moelski
Aber gut, dann würde man die Daten sammeln und in einem eigenen Thread nach x Sekunden / Minuten / ... in die DB schieben.

Dem ist eigentlich nichts hinzuzufügen, nur eine kleine Anmerkung:
jedes Insert/Update ist von einer Transaktion "umhüllt". Je größer die Datenmenge innerhalb der Transaktion, desto größer der Verwaltungsaufwand pro Transaktion (Rollback) darum würde ich die Menge nicht ins "unendliche" wachsen lassen.

Gruß
K-H

moelski 8. Mär 2010 13:04

Re: Schnellste Insert Möglichkeit für eine DB?
 
Moin !

Zitat:

Je größer die Datenmenge innerhalb der Transaktion, desto größer der Verwaltungsaufwand pro Transaktion (Rollback)
Hmm aber hat nicht jemand ein paar Postings vorher gesagt das man sowas gerade in Transaktionen Hüllen sollte? Widerspricht sich das nicht ein bisschen?

Zitat:

darum würde ich die Menge nicht ins "unendliche" wachsen lassen
Was wäre denn ein verträgliches Mass?

p80286 8. Mär 2010 13:21

Re: Schnellste Insert Möglichkeit für eine DB?
 
nocheinmal
Jedes INSERT/UPDATE-Statement steckt in einer Transaktion (Naja so sollte es eigentlich sein).
Und bei einigen Systemen hast Du es in der Hand wann die Transaktion mit Commit/Autocommit abgeschlossen wird.

Wie exilant schrieb:
Zitat:

Das geht granatenschnell und läuft hier sehr zuverlässig.
Ist natürlich von der Umgebung und der Datenmenge abhängig. Ggf. solltest Du das mal ausprobieren wie Dein System darauf reagiert.

Eine Faustformel wie "1000 Datensätze und dann ab damit" halte ich für nicht sehr vernünftig.
Ich rechne immer mit "die Transaktion ist abgeschlossen" die Daten sind gespeichert. Wenn Du die Daten in einer Datei zwischenspeicherst, dann ist das ja nicht ganz so wichtig.
(Wieso kann man Deine Meßwerte eigentlich nicht in einer Datei zwischenspeichern? ob server oder embedded hat doch damit nichts zu tun. Oder gibt's da ein Problem mit dem Plattenplatz????)

Gruß
K-H

exilant 8. Mär 2010 13:47

Re: Schnellste Insert Möglichkeit für eine DB?
 
Zitat:

Zitat von moelski
Moin !
Was wäre denn ein verträgliches Mass?

Ich habe manchmal 8.000 und mehr "inserts" in einer Transaktion. Das hat noch nie zu Problemen geführt.
Das ist dank Firebirds MGA auch kein sooo wahnsinniger Overhead für die DB. Da würde ich mir keine Sorgen machen.
Und wie ich schon sagte: Es ist schnell.

Delphi-Quellcode:

insertstatement.transaction.starttransaction;

try
  readcsv;
  while not eof(csv)do begin
    insertstatement.paramby.... := ...csv_value....
    insertstatement.paramby.... := ...csv_value....
    insertstatement.paramby.... := ...csv_value....
    ...
    insertstatement.execute;
    readcsv;
  end;
  insertstatement.transaction.commit;
  killcsv;
except
  on e:exception do begin
    insertstatement.transaction.rollback;
    WhateverYouMustDo;
  end;
end;

xZise 8. Mär 2010 14:03

Re: Schnellste Insert Möglichkeit für eine DB?
 
Moin,
ich habe dazu mal eine Frage, weil mir fällt da das Einlesen einer Schülerliste ein. Das heißt ich habe eine Datenbank mit Personen und dann bekomme ich eine neue Liste mit Personen. Jetzt will ich neue Personen hinzufügen, und alte (also die nicht in der neuen Liste stehen) löschen. Aber ich will nicht die komplette Liste neu anlegen.

Nun ist das aktuell so, dass jeder ein Flag bekommt, ob er in der Liste ist und standard ist das keiner. Dann geht er jede Person auf der Liste durch und vergleicht das mit der Datenbankliste und setzt das Flag auf „vorhanden“. Wenn das nicht der Fall ist, wird die Person hinzufügt. Kann ich jetzt zu Anfang ein SQL Befehl erstellen (präventiv) und das dann nutzen, auch wenn zwischenzeitlich mal ein Update kommt?
Ich benutze SQLite, und hoffe mal, dass ich nicht geschlagen werde, dass ich keinen neuen Thread auf mache :)

MfG
Fabian


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:26 Uhr.
Seite 1 von 2  1 2      

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