Entwickler-Ecke
Datenbanken - Nummernbereich Von Bis einfügen
Bronstein - Fr 05.08.11 06:30
Titel: Nummernbereich Von Bis einfügen
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?
Xion - Fr 05.08.11 06:45
glaube nicht dass das ohne Schleife geht. (du kannst dir natürlich eine Funktion außenrum bauen)
Aber um die Geschwindigkeit zu steigern:
http://msdn.microsoft.com/en-us/library/aa905921%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.
ZeitGeist87 - Fr 05.08.11 08: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; |
baka0815 - Do 02.02.12 13: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 - Do 02.02.12 14:58
...lässt sich sicher beschleunigen...
baka0815 - Do 02.02.12 18: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 - Do 02.02.12 19: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!
baka0815 - Fr 03.02.12 11: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 - Fr 03.02.12 13: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; |
Martok - Fr 10.02.12 15:37
Hinweis:
Diskussion über SQL-Injections abgetrennt nach hier
hier [
http://www.delphi-forum.de/viewtopic.php?t=108489].
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 |
baka0815 - Fr 10.02.12 17: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 - Fr 10.02.12 21: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 ;)
olliterski - Sa 11.02.12 23:49
@Martok:
Hast du doch schon in deinem vorletzten Beitrag geschrieben was hier drin stand!
;)
baka0815 - Mo 13.02.12 12: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 - Mo 13.02.12 14: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 ;-)
baka0815 - Di 14.02.12 11: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
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!