Autor |
Beitrag |
Bronstein
      
Beiträge: 578
Erhaltene Danke: 1
WIN XP
Delphi 6 / Delphi 2006 / Delphi XE
|
Verfasst: 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
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
      

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)
|
Verfasst: 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
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; 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
      
Beiträge: 1593
Erhaltene Danke: 20
Win95-Win10
Delphi 10 Seattle, Rad Studio 2007, Delphi 7 Prof., C++, WSH, Turbo Pascal, PHP, Delphi X2
|
Verfasst: Fr 05.08.11 09:40
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; 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
      
Beiträge: 489
Erhaltene Danke: 14
Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
|
Verfasst: 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:
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 = 0) do begin 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 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
      
Beiträge: 105
Erhaltene Danke: 2
Win7 64-Bit
D7 Ent.
|
Verfasst: 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
      
Beiträge: 489
Erhaltene Danke: 14
Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
|
Verfasst: 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
      
Beiträge: 105
Erhaltene Danke: 2
Win7 64-Bit
D7 Ent.
|
Verfasst: 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
      
Beiträge: 489
Erhaltene Danke: 14
Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
|
Verfasst: 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:
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
      
Beiträge: 1593
Erhaltene Danke: 20
Win95-Win10
Delphi 10 Seattle, Rad Studio 2007, Delphi 7 Prof., C++, WSH, Turbo Pascal, PHP, Delphi X2
|
Verfasst: Fr 03.02.12 14:23
Oder du nutzt, falls das DBMS es unterstützt Transatkionen.
Pseudo
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;
query.sql.text:= '<InsertStatement>'; query.ExecSQL; 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
      
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Fr 10.02.12 16:37
Hinweis:
Diskussion über SQL-Injections abgetrennt nach hier hier.
Ein paar Zeilen gehörten noch hier rein:
olliterski hat folgendes geschrieben : | 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
      
Beiträge: 489
Erhaltene Danke: 14
Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
|
Verfasst: 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
      
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: 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
      
Beiträge: 105
Erhaltene Danke: 2
Win7 64-Bit
D7 Ent.
|
Verfasst: 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
      
Beiträge: 489
Erhaltene Danke: 14
Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
|
Verfasst: 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
      
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Mo 13.02.12 15:45
baka0815 hat folgendes geschrieben : | 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
      
Beiträge: 489
Erhaltene Danke: 14
Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
|
Verfasst: 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. 
|
|
|