Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Delphi Insert-Schleife viel zu langsam (https://www.delphipraxis.net/164838-insert-schleife-viel-zu-langsam.html)

Partikelecho 2. Dez 2011 07:20

Datenbank: DBISAM • Version: 4 • Zugriff über: DBISAMQuery

Insert-Schleife viel zu langsam
 
Guten Morgen,
Ich bin neu hier, also verzeiht mir eventuell falsch genutzte Foren, o.ä. fürs Erste.

Meine Aufgabe ist das Programmieren einer Anwendung zum Auslesen von Ordnern (welche immer eine Datenbank sind, also Pfad = Datenbank) und das darstellen der Dateiinformationen in einem DBGrid.

Ich verwende Delphi 6 und soweit ich weiß DBISAM 4 (kann ich im moment nicht überprüfen).
Mein Problem ist, dass zwar die FindAllFiles-Funktion und die Variablenzuweisung in der Schleife relativ schnell gehen, das Inserten von 40.000 Datensätzen in etwa 8 bis 10 Minuten dauert.

- Version mit Parametern statt direkter Variablenübergabe ist noch langsamer
- Bulkinsert von jeweils 50, 100 oder 1000 SQL-Inserts dauert in etwa genau so langsam.
- Die Funktionen GetFileTimes & GetFileSize laufen auch nur ein paar Sekunden.
- Kommentiere ich den SQL-Teil ab with aus, so dauert es ebenfalls nur ca. 10 Sekunden (gut, immernoch zu lange, aber im Vergleich zu Minuten..)
- SL[i] (eine Stringlist) enthält die einzufügenden Dateien.

Delphi-Quellcode:
procedure TFRM_Main.ReadInInvA;
var
  i, invasize:Integer;
  inva, invaaccessed, invacreated, invamodified:String;
  created, accessed, modified:TDateTime;
begin
  QRY_Container.SQL.Clear;
  inva := invaname;
  for i := 0 to SL.Count-1 do
  begin
    if GetFileTimes(CXBE_DbDir.Text+’\‘SL[i], created, accessed, modified) then
    begin
      invaaccessed := DateTimeToStr(accessed);
      invacreated := DateTimeToStr(created);
      invamodified := DateTimeToStr(modified);
    end;
    invasize := GetFileSize(CXBE_DbDir.Text’\‘+SL[i]);
    with QRY_Container do
    begin
      SQL.Add(’INSERT INTO ‘inva’(INVA_FILE, INVA_FILE_CREATED,‘+
              ’INVA_FILE_ALTERED, INVA_FILE_VIEWED, INVA_FILE_SIZE) VALUES’+
              ’ (’’’+SL[i]‘’’,‘’’invacreated+’’’,‘+
              ’’’’+invamodified+’’’,’’’+invaaccessed+’’’,‘inttostr(invasize)’);’);
      if i > 0 then
      begin
        if (i mod 1000) = 0 then
        begin
          ExecSQL;
          SQL.Clear;
        end;
      end;
      if i = SL.Count-1 then
      ExecSQL;
      // 1000 Insert-Befehle werden hintereinander geschrieben und dann zusammen ausgeführt.
    end;
  end;
end;

Bernhard Geyer 2. Dez 2011 07:44

AW: Insert-Schleife viel zu langsam
 
Prepared Statements mit Parameter sollten schneller sein.
DBISAM ist eine Destkop-Datenbank. Hier solltest du deinen Virenscanner so einstellen das die enstprechenden DB-Dateien nicht untersucht weden.

Wie viele Indizes gibt es auf deiner Tabelle. Je mehr desto langsamer ist auch ein Insert. Hier bremst im gegensatz zu Abfragen jeder Index.

tankard 2. Dez 2011 08:28

AW: Insert-Schleife viel zu langsam
 
hi,

also so kann das nur langsam sein.
da bei jedem insert die daten direkt auf platte geschrieben werden.
schau mal ob das datenbanksystem auch transactions kann. mal nach googlen was das ist. http://www.firstsql.com/tutor5.htm

gruss
tankard

ps: nur weil du 1000 sql statements zusammenfuegst, werden diese doch der reihe nach abgearbeitet, insert befehle ohne transaction, werden direkt auf platte geschrieben. daher hast du bei 1000 inserts, 1000 plattenzugriffe. wenn du tranaktionen nutzt, wird nach einem commit nur einmal alles weggeschrieben.

Medium 2. Dez 2011 09:03

AW: Insert-Schleife viel zu langsam
 
Es ist auch erheblich schneller, mehrere Sätze in einem Insert zu machen:
Code:
INSERT INTO foo VALUES (bar1, peng1), (bar2, peng2), (bar3, ...
Wie viele Sätze man in so ein Statement hängen kann, hängt vom DBMS ab (bzw. ist spätestens nach 2GB wegen Stringlänge Ende ;)), aber ich hab mit 100 am Stück zumindest noch nie Probleme bekommen, und die reichten mir bislang für einen deftigen boost.

Partikelecho 7. Dez 2011 06:49

AW: Insert-Schleife viel zu langsam
 
Hallo,
Ich hab nun endlich wieder Zugriff auf meine Source gehabt und wollte euch noch zeigen, wie ich die Zeit massiv verkürzen konnte, in der die Dateien eingelesen werden.

tankards Vorschlag mit Transactions zu arbeiten war zielführend.
Zusätzlich habe ich den Insertpart durch AppendRecord ersetzt.
Falls ihr noch Verbesserungsvorschläge habt, wie ich die Zeit weiter verkürzen könnte, dann immer her damit.

Danke für eure Hilfe..

Delphi-Quellcode:
procedure TFRM_Main.ReadInInvA;
var
  i, invasize:Integer;
  invaaccessed, invacreated, invamodified:String;
  created, accessed, modified:TDateTime;
begin
  DB_Container.Directory := CXBE_InvDir.Text;
  DB_Container.Open;
  TBL_Container.DatabaseName := DB_Container.DatabaseName;
  DB_Container.StartTransaction;
  for i := 0 to SL.Count-1 do
  begin
    if GetFileTimes(CXBE_DbDir.Text+’\‘+SL[i], created, accessed, modified) then
    begin
      invaaccessed := DateTimeToStr(accessed);
      invacreated := DateTimeToStr(created);
      invamodified := DateTimeToStr(modified);
    end;
    invasize := GetFileSize(CXBE_DbDir.Text+’\’+SL[i]);
    with TBL_Container do
    begin
      DatabaseName := DB_Container.DatabaseName;
      TableName := invaname;
      Open;
      AppendRecord([null, SL[i], invacreated, invamodified, invaaccessed, invasize]);
      Close;
    end;
  end;
  DB_Container.Commit;
  DB_Container.Close;
end;

himitsu 7. Dez 2011 07:03

AW: Insert-Schleife viel zu langsam
 
Zitat:

Delphi-Quellcode:
      DatabaseName := DB_Container.DatabaseName;
      TableName := invaname;
      Open;
      ...
      Close;

Diese Teile ändern sich nicht.
Wieso also nicht einfach nur einmal for/nach der For-Schleife?

Statt der vielen FileTime- und FileSize-Funktionen könnte man es mal mit einem MSDN-Library durchsuchenFindFirstFile versuchen. (das ganze Geraffel vom Delphi-Referenz durchsuchenFindFirst kann man sich hier sparen)


PS: SL ist eine sehr gut benamte externe Variable. :thumb:

Partikelecho 7. Dez 2011 07:11

AW: Insert-Schleife viel zu langsam
 
Zitat:

Zitat von himitsu (Beitrag 1139928)
Zitat:

Delphi-Quellcode:
      DatabaseName := DB_Container.DatabaseName;
      TableName := invaname;
      Open;
      ...
      Close;

Diese Teile ändern sich nicht.
Wieso also nicht einfach nur einmal for/nach der For-Schleife?

Aber hat dieser Teil dann nicht nur die Werte des letzten Schleifendurchlaufs? Es werden doch immer jeweils die Variablen neu erstellt und für jede Datei neu hinzugefügt.

Zitat:

Zitat von himitsu (Beitrag 1139928)
Statt der vielen FileTime- und FileSize-Funktionen könnte man es mal mit einem MSDN-Library durchsuchenFindFirstFile versuchen. (das ganze Geraffel vom Delphi-Referenz durchsuchenFindFirst kann man sich hier sparen)


PS: SL ist eine sehr gut benamte externe Variable. :thumb:

Damit werde ich mich noch auseinandersetzen, ich hatte ohnehin vor, diese Funktionen noch anzupassen/ zu verbessern.

Naja gut, SL wird in dem Programm immer mal wieder verwendet, es hat einen allgemeinen Zweck und wird nicht stetig gebraucht. Deswegen ist der Bezeichner SL so geblieben. ;)

himitsu 7. Dez 2011 07:23

AW: Insert-Schleife viel zu langsam
 
AppendRecord macht
Delphi-Quellcode:
Append;
// Felder setzen
Post;
und das geht doch wohl auch mehrmals hintereinande, ohne jedesmal die Tabelle neu auf+zu zumachen?

Eventuell auch noch ein DisableControls/EnableControls drumrum.


Zitat:

Naja gut, SL wird in dem Programm immer mal wieder verwendet, es hat einen allgemeinen Zweck und wird nicht stetig gebraucht. Deswegen ist der Bezeichner SL so geblieben.
Also eine unbestimmte globale Variable, bei der man nie genau weiß, was sich nun gerade dort drin befindet? :stupid:

Sir Rufo 7. Dez 2011 08:29

AW: Insert-Schleife viel zu langsam
 
Zitat:

Zitat von himitsu (Beitrag 1139934)
Zitat:

Naja gut, SL wird in dem Programm immer mal wieder verwendet, es hat einen allgemeinen Zweck und wird nicht stetig gebraucht. Deswegen ist der Bezeichner SL so geblieben.
Also eine unbestimmte globale Variable, bei der man nie genau weiß, was sich nun gerade dort drin befindet? :stupid:

Hierbei handelt es sich um das StringLuder. Da darf jeder mal ran und was reinstecken und man weiß eigentlich nie, was man so richtig bekommt, aber meistens geht hoffentlich alles gut. :mrgreen:

DeddyH 7. Dez 2011 08:30

AW: Insert-Schleife viel zu langsam
 
[OT] Ich dachte, das hieße Santa Laus :mrgreen: [/OT]

himitsu 7. Dez 2011 09:29

AW: Insert-Schleife viel zu langsam
 
Santa Laus ... ein Sack voller Überraschungen :angle:

Partikelecho 9. Dez 2011 07:18

AW: Insert-Schleife viel zu langsam
 
Na ihr habt ja den sprichwörtlichen Clown verschluckt ;)

Ich habe die Prozedur noch ein bisschen beschleunigen können, nach dem himitsu meinen Knoten im Schlauch entfernt hat.

Zum Thema StringLuder, die StringList wird immer ordnungsgemäß nach ihrem Gebrauch frei gegeben und sie wird nicht gleichzeitig an mehreren Stellen verwendet. Ich habe 3 unterschiedliche Einleseprozesse, welche aber noch ineinander integriert werden sollen. Die StringList hat dann nur noch eine Aufgabe und dann kann ich sie bequem gemäß ihrem Zweck benennen.

Delphi-Quellcode:
procedure TFRM_Main.ReadInInvA;
var
  i, invasize:Integer;
  invaaccessed, invacreated, invamodified:String;
  created, accessed, modified:TDateTime;
begin
  DB_Container.Close;
  DB_Container.Directory := CXBE_InvDir.Text;
  DB_Container.Open;
  TBL_Container.DatabaseName := DB_Container.DatabaseName;
  TBL_Container.TableName := invaname;
  DB_Container.StartTransaction;
  TBL_Container.Open;
  TBL_Container.DisableControls;
  for i := 0 to SL.Count-1 do
  begin
    if GetFileTimes(CXBE_DbDir.Text+'\'+SL[i], created, accessed, modified) then
    begin
      invaaccessed := DateTimeToStr(accessed);
      invacreated := DateTimeToStr(created);
      invamodified := DateTimeToStr(modified);
    end;
    invasize := GetFileSize(CXBE_DbDir.Text+'\'+SL[i]);
    TBL_Container.AppendRecord([null, SL[i], invacreated, invamodified, invaaccessed, invasize]);
  end;
  TBL_Container.EnableControls;
  TBL_Container.Close;
  DB_Container.Commit;
  DB_Container.Close;
end;
Als nächstes sollte ich mir wohl mal über eine einfachere Lösung zum Thema GetFileSize und GetFileTimes machen,
allerdings muss ich mich dazu noch einmal genau belesen.. Sinnvoll wäre es doch eventuell diese Informationen über eine Funktion auszulesen, oder?

Außerdem könnte ich bei AppendRecord genausogut DateTimeToStr(created) usw eintragen, anstatt erst Variablen zu befüllen.

p80286 9. Dez 2011 08:38

AW: Insert-Schleife viel zu langsam
 
Was passiert eigentlich wenn
Delphi-Quellcode:
GetFileTimes
fehlschlägt?

Gruß
K-H

Partikelecho 9. Dez 2011 20:19

AW: Insert-Schleife viel zu langsam
 
Das ist richtig, ich hab bisher noch keine Fehlerbehandlungsroutinen eingebaut. Mir geht es zunächst um die Performance und Logik der wichtigsten Funktionen.
Die Fehlerbehandlung ist aber schon vorgemerkt.

Delphi-Quellcode:
procedure TFRM_Main.ReadInInvA;
var
  i:Integer;
  created, accessed, modified:TDateTime;
begin
...
  for i := 0 to SL.Count-1 do
  begin
    GetFileTimes(CXBE_DbDir.Text+'\'+SL[i], created, accessed, modified);
    TBL_Container.AppendRecord([null, SL[i], DateTimeToStr(created), DateTimeToStr(modified), DateTimeToStr(accessed), GetFileSize(CXBE_DbDir.Text+'\'+SL[i])]);
  end;
...
end;

Furtbichler 10. Dez 2011 05:09

AW: Insert-Schleife viel zu langsam
 
Zitat:

Zitat von Partikelecho (Beitrag 1140673)
Das ist richtig, ich hab bisher noch keine Fehlerbehandlungsroutinen eingebaut.

Gewöhne Dir mal an, das sofort zu implementieren. Langsamer wird es nicht und man hat sofort robusten Code.


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