Autor Beitrag
Bronstein
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 578
Erhaltene Danke: 1

WIN XP
Delphi 6 / Delphi 2006 / Delphi XE
BeitragVerfasst: Fr 05.08.11 07:30 
Hallo,
gibt es eine Möglichkeit das folgende zu vereinfachen:

Ich habe z.B. einen Nummernbereich von 1-1000000 und möchte diesen jetzt einfügen

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
for i:=1 to 1000000 do
begin
  AdoCommand.CommandText := 'Insert INTO DATEN (NUMMER, TEXT) VALUES ('+IntToStr(i)+ ', ' + QuotedStr(tmpText) +')'
  AdoCommand.Execute;
end;


Geht das auch ohne diese Schleife?

_________________
Es gibt keine dummen Fragen nur dumme Antworten!!!
Xion
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
EE-Maler
Beiträge: 1952
Erhaltene Danke: 128

Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
BeitragVerfasst: Fr 05.08.11 07:45 
glaube nicht dass das ohne Schleife geht. (du kannst dir natürlich eine Funktion außenrum bauen)

Aber um die Geschwindigkeit zu steigern:
msdn.microsoft.com/e...1%28v=sql.80%29.aspx

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
ADOConnection.BeginTrans;
for i:=1 to 1000000 do
begin
  AdoCommand.CommandText := AdoCommand.CommandText + 'Insert INTO DATEN (NUMMER, TEXT) VALUES ('+IntToStr(i)+ ', ' + QuotedStr(tmpText) +');'
end;
//AdoCommand.Execute; //vielleicht noch nötig, mal ausprobieren
ADOConnection.CommitTrans;


Vielleicht gehts auch ohne dem Begin/Commit Trans, wenn du einfach alles in einen String reinschreibst. Probiers mal aus und sag, obs hilft :)

Es kann auch sein, dass du jedes Statement für sich "Executen" musst.

_________________
a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)
ZeitGeist87
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1593
Erhaltene Danke: 20

Win95-Win10
Delphi 10 Seattle, Rad Studio 2007, Delphi 7 Prof., C++, WSH, Turbo Pascal, PHP, Delphi X2
BeitragVerfasst: Fr 05.08.11 09:40 
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
AdoCommand.CommandText := 'Insert INTO DATEN (NUMMER, TEXT) VALUES ';
for i:=1 to 1000000 do
begin
  AdoCommand.CommandText:= AdoCommand.CommandText + '('+IntToStr(i)+ ', ' + QuotedStr(tmpText) +'), ';  
end;
//Vorher hinten das letzte Komma abschneiden ;-)
AdoCommand.Execute;

_________________
Wer Provokationen, Ironie, Sarkasmus oder Zynismus herauslesen kann soll sie ignorieren um den Inhalt meiner Beiträge ungetrübt erfassen zu können.
baka0815
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 489
Erhaltene Danke: 14

Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
BeitragVerfasst: Do 02.02.12 14:17 
Ich habe ein ähnlich gelagertes Problem und zwar geht es um das Importieren von Daten in eine Datenbank.
Die Daten müssen hierbei allerdings umgewandelt, geprüft und verarbeitet werden, weswegen der Weg über BULK INSERT nicht möglich ist. Außerdem soll das ganze unter Oracle und MS-SQL funktionieren.

Der Import ist so aufgebaut, dass ich pro Datenbanktabelle eine eigene Datei habe, die zeilenweise ausgelesen wird. Im Prinzip sind es CSV-Dateien mit einem Tabulator als Trenner.

Das ganze Programm läuft (leider und aus verschiedenen historischen Gründen) über die BDE.

Ich habe ein TQuery-Objekt, in welchem ich das SQL mit Parametern setze, das ganze dann mit Prepare vorbereite und dann in der Schleife nur noch die Parameter setze und das INSERT per ExecSQL an die DB sende.
Mit Zeitmessungen habe ich festgestellt, dass bei einem Import der 277 Sekunden dauert, 200 Sekunden alleine für das ExecSQL drauf gehen (also ~70%). Nachdem ich jetzt Transaktionen pro Tabelle eingebaut habe, dauert derselbe Vorgang nur noch 133 Sekunden, allerdings sind noch immer etwa 70% davon ExecSQL.


Exemplarischer Code:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
  Ok = FindFirst();
  while (Ok = 0do
  begin
    // SQL mit Parametern anhand der Struktur in der ersten Zeile der Datei zusammenbauen
    ReadLn(Ueberschriften, Datei);
    
    Query.SQL.Clear;
    Query.SQL.Add('insert into Tabelle (A, B, C) values (:A, :B, :C)');

    while not Eof(Datei) do
    begin
      // Einzelne Werte aus der Datei lesen
      Wert := LeseWert(Datei);

      if (Spalte = ftInteger) then
        Query.ParamByName(SpaltenName).AsInteger := StrToInt(Wert)
      else if (Spalte = ftString) then
        Query.ParamByName(SpaltenName).AsInteger := Wert;

      Query.ExecSQL;
    end;

    Ok := FindNext();
  end;


Kann man das ganze noch weiter beschleunigen?
olliterski
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 105
Erhaltene Danke: 2

Win7 64-Bit
D7 Ent.
BeitragVerfasst: Do 02.02.12 15:58 
...lässt sich sicher beschleunigen...

_________________
Viele Grüße
Oliver


Zuletzt bearbeitet von olliterski am Mo 13.02.12 00:24, insgesamt 1-mal bearbeitet
baka0815
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 489
Erhaltene Danke: 14

Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
BeitragVerfasst: Do 02.02.12 19:10 
Nachteil daran ist, dass du keine Parameter nutzen kannst und daher jeden SQL-Text immer schön selbst escapen musst - oder du öffnest SQL Injections Tür und Tor.
Für mich ist das auch nichts, da durchaus auch Binärdaten importiert werden müssen - in den Textdateien sind diese dann Base64 kodiert.

Das Sperren der Eingabedateien ist für mich kein Problem, da diese genau für diesen Import-Vorgang zur Verfügung stehen und die Gefahr dass andere genau auf dieselben Daten zum Import zugreifen ist ziemlich genau bei 0. Dateisperren sind also gar kein Problem.
olliterski
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 105
Erhaltene Danke: 2

Win7 64-Bit
D7 Ent.
BeitragVerfasst: Do 02.02.12 20:09 
Yepp, wäre halt ein anderer Ansatz, eine andere Idee.
Ob man sie für seinen speziellen Fall verwenden kann ist eine ander Frage!

_________________
Viele Grüße
Oliver


Zuletzt bearbeitet von olliterski am Mo 13.02.12 00:53, insgesamt 1-mal bearbeitet
baka0815
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 489
Erhaltene Danke: 14

Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
BeitragVerfasst: Fr 03.02.12 12:43 
Du nutzt keine Parameter, sondern baust dir das SQL immer neu mit den Werten zusammen.

Ich meine Parameter in Verbindung mit einem PreparedStatement:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
  Query.SQL.Add('insert into Tabelle (SpalteA, SpalteB)');
  Query.SQL.Add('values (:WertA, :WertB)');

  Query.Prepare;

  for I := 0 to 100000 do
  begin
    Query.ParamByName('WertA').AsInteger := I;
    Query.ParamByName('WertB').AsString := IntToStr(I);
    
    Query.ExecSQL;
  end;


Das ist das, was ich zur Datenbanktheorie gelernt habe. Wenn man ein und dasselbe SQL mehrfach hintereinander ausführt das Query mit Parametern aufbauen, Vorbereiten und dann nur noch Parameter setzen und ausführen.

Und was die SQL-Injections betrifft ist das natürlich nicht Sache des DBA, sondern des Softwareentwicklers. Ich importiere schließlich Textdaten, wenn da jetzt jemand - warum auch immer - den Eintrag "'; DROP DATABASE XYZ;" einfügen würde, würde er in deinem Fall die Datenbank droppen. Was von dem Programm sicherlich nicht gewollt ist.
Zwar läuft mein Tool nicht in Produktivumgebungen sondern bei uns im Support um Kundendaten einzuspielen, trotzdem soll dies natürlich so "sicher wie möglich" sein, ohne dass jemand Schindluder treiben kann.
ZeitGeist87
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1593
Erhaltene Danke: 20

Win95-Win10
Delphi 10 Seattle, Rad Studio 2007, Delphi 7 Prof., C++, WSH, Turbo Pascal, PHP, Delphi X2
BeitragVerfasst: Fr 03.02.12 14:23 
Oder du nutzt, falls das DBMS es unterstützt Transatkionen.

Pseudo :-)

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
try
 query.sql.text:= 'BEGIN TRANSACTION';
 query.ExecSQL;

 //SChleife
 query.sql.text:= '<InsertStatement>';
 query.ExecSQL;
 //Schleife-Ende

 query.sql.text:= 'COMMIT WORK';
 query.ExecSQL;
except
 query.sql.text:= 'ROLLBACK WORK';
 query.ExecSQL;
end;

_________________
Wer Provokationen, Ironie, Sarkasmus oder Zynismus herauslesen kann soll sie ignorieren um den Inhalt meiner Beiträge ungetrübt erfassen zu können.
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Fr 10.02.12 16:37 
Hinweis:
Diskussion über SQL-Injections abgetrennt nach hier hier.

Ein paar Zeilen gehörten noch hier rein:
user profile iconolliterski hat folgendes geschrieben Zum zitierten Posting springen:
Moin,

Parameter...ja ich stand auf der Leitung! ;)
Hatte auch das Prepare überlesen!

{...}

Viele Grüße

Oliver

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
baka0815
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 489
Erhaltene Danke: 14

Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
BeitragVerfasst: Fr 10.02.12 18:15 
Die Syntax rollback work bzw commit work ist mir neu, welches DBMS ist das? Ich kenne es nur ohne das work.

Habe bei mir auch Transaktionen eingebaut und war erstaunt, dass es immerhin knapp 30% schneller geht. Ich hatte vermutet, dass der Overhead um die Transaktionen dann zu synchronisieren größer sei, aber so kann man sich täuschen.
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Fr 10.02.12 22:31 
Bei Oracle wird sich der Automatic Database Diagnostic Monitor (ADDM) beschweren, wenn man solche Massen-Abfragen nicht mit Parametern macht. Dann dauert nämlich das Parsen des SQL-Text länger als die Verarbeitung und der merkt das. Gibt dann schöne Meldungen in der Admin-Konsole, man solle doch mal gucken was man da verzapft hat.

Man will also eigentlich immer Parameter, auch wenn die in Delphi etwas seltsam realisiert sind. In Pythons DBI ist das schöner, da kann man gar nicht versehentlich was anderes verwenden, weil das so einfach ist ;)

Man kann auch noch (je nach DB-Binding) Parameter-Arrays übergeben, so dass man dann sagt "Hier hast du ein Statement und 2x1000000 Variablen, mach mal". Ob man das aber mit ADO hinbekommt ist die andere Frage.

Aber 1 Mio Werte, das darf schon mal ein bisschen dauern. Wenn du mal 10 Minuten auf 97MB Access-DB durchnudeln gewartet hast, weißt du was langsam ist ;)

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
olliterski
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 105
Erhaltene Danke: 2

Win7 64-Bit
D7 Ent.
BeitragVerfasst: So 12.02.12 00:49 
@Martok:
Hast du doch schon in deinem vorletzten Beitrag geschrieben was hier drin stand!
;)

_________________
Viele Grüße
Oliver
baka0815
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 489
Erhaltene Danke: 14

Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
BeitragVerfasst: Mo 13.02.12 13:59 
Ich finde Parameter in Delphi relativ simpel und gut gelöst - ich mag benannte Parameter.

Bei Java hingegen ist's ein Graus. Jeder Parameter ein Fragezeichen und dann zählen - ätzend. Vor allem wenn man dann in der Mitte einen weiteren Parameter einfügt...
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Mo 13.02.12 15:45 
user profile iconbaka0815 hat folgendes geschrieben Zum zitierten Posting springen:
Ich finde Parameter in Delphi relativ simpel und gut gelöst - ich mag benannte Parameter.

Recht hast du. Ich meinte eher das vorherige Binden: in Python kann ich einfach beim Ausführen einen "Hash" mit Name-Wert-Paaren übergeben. Delphi fehlen eben echte assoziative Arrays, jenseits von der COM-Lösung immer Name und Wert abwechselnd in ein Array zu stecken.

Hatte mal für php-mysqli einen Wrapper geschrieben, der benannte Parameter live in "?" übersetzt. Kenne dein Leid also ;-)

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
baka0815
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 489
Erhaltene Danke: 14

Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
BeitragVerfasst: Di 14.02.12 12:09 
Das kann man sich aber doch recht einfach bauen, über ein array of const.
Da ist nur der Nachteil, dass man Double und TDateTime nicht mehr auseinanderhalten kann und daher nicht weiß ob .AsCurrency oder .AsDateTime... Also vielleicht doch keine Lösung. :D