Entwickler-Ecke

Datenbanken - Warum so langsam?


D. Annies - Di 07.07.09 20:13
Titel: Warum so langsam?
Hi, Delpher,

warum ist der folgende Code, durch den eine Merge-Datei entsteht, so langsam - ca. 2 sec pro Datensatz??


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:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
procedure TForm1.Button28Click(Sender: TObject);
var  InputString : string;
begin
  InputString := 'merge.dbf';
  Inputstring := InputBox('Merge-Filename''Dateiname:', Inputstring);
  with form2.table1 do
  begin
    Active := False;  //DatabaseName := '';
      TableName := Inputstring;
      TableType := ttDefault;  //ttDBase;
      FieldDefs.Clear;
      FieldDefs.Add('Name',       ftString,   25, False);
      FieldDefs.Add('Vorname',    ftString,   25, False);
      FieldDefs.Add('Klasse',     ftString,    5, False);
      FieldDefs.Add('Geschlecht', ftString,    1, False);
      FieldDefs.Add('Gebdat',     ftString,   10, False); //ftDate ??
      FieldDefs.Add('Punkte',     ftinteger);
    CreateTable;
  end;
  form2.table1.active := true;

  table1.Close;
    table1.TableName := concat(home, '\''R1x.dbf');
  table1.active := true;
  table1.First;
  while not table1.Eof do
  begin
    form2.Table1.Append;
      form2.Table1.fieldbyname('NAME').asstring :=       table1.fieldbyname('NAME').asstring;
      form2.Table1.fieldbyname('VORNAME').asstring :=    table1.fieldbyname('VORNAME').asstring;
      form2.Table1.fieldbyname('KLASSE').asstring :=     table1.fieldbyname('KLASSE').asstring;
      form2.Table1.fieldbyname('GESCHLECHT').asstring := table1.fieldbyname('Geschlecht').asstring;
      form2.Table1.fieldbyname('GEBDAT').asstring :=     table1.fieldbyname('gebdat').asstring;
      form2.Table1.fieldbyname('PUNKTE').asinteger :=    table1.fieldbyname('Punkte').asinteger;
    form2.table1.Post;
    table1.Next;
  end;
  showmessage('merge '+ table1.TableName + ' ok');
  table3.Close;
    table3.TableName := concat(home, '\''R7x.dbf');
  table3.active := true;
  table3.First;
  while not table3.Eof do
  begin
    form2.Table1.Append;
      form2.Table1.fieldbyname('NAME').asstring :=       table3.fieldbyname('NAME').asstring;
      form2.Table1.fieldbyname('VORNAME').asstring :=    table3.fieldbyname('VORNAME').asstring;
      form2.Table1.fieldbyname('KLASSE').asstring :=     table3.fieldbyname('KLASSE').asstring;
      form2.Table1.fieldbyname('GESCHLECHT').asstring := table3.fieldbyname('Geschlecht').asstring;
      form2.Table1.fieldbyname('GEBDAT').asstring :=     table3.fieldbyname('gebdat').asstring;
      form2.Table1.fieldbyname('PUNKTE').asinteger :=    table3.fieldbyname('Punkte').asinteger;
    form2.table1.Post;
    table3.Next;
  end;
  showmessage('merge '+ table3.TableName + ' ok');
  table4.Close;
    table4.TableName := concat(home, '\''R8x.dbf');
  table4.active := true;
  table4.First;
  while not table4.Eof do
  begin
    form2.Table1.Append;
      form2.Table1.fieldbyname('NAME').asstring :=       table4.fieldbyname('NAME').asstring;
      form2.Table1.fieldbyname('VORNAME').asstring :=    table4.fieldbyname('VORNAME').asstring;
      form2.Table1.fieldbyname('KLASSE').asstring :=     table4.fieldbyname('KLASSE').asstring;
      form2.Table1.fieldbyname('GESCHLECHT').asstring := table4.fieldbyname('Geschlecht').asstring;
      form2.Table1.fieldbyname('GEBDAT').asstring :=     table4.fieldbyname('gebdat').asstring;
      form2.Table1.fieldbyname('PUNKTE').asinteger :=    table4.fieldbyname('Punkte').asinteger;
    form2.table1.Post;
    table4.Next;
  end;
  showmessage('merge '+ table4.TableName + ' ok');
  table5.Close;
    table5.TableName := concat(home, '\''R9x.dbf');
  table5.active := true;
  table5.First;
  while not table5.Eof do
  begin
    form2.Table1.Append;
      form2.Table1.fieldbyname('NAME').asstring :=       table5.fieldbyname('NAME').asstring;
      form2.Table1.fieldbyname('VORNAME').asstring :=    table5.fieldbyname('VORNAME').asstring;
      form2.Table1.fieldbyname('KLASSE').asstring :=     table5.fieldbyname('KLASSE').asstring;
      form2.Table1.fieldbyname('GESCHLECHT').asstring := table5.fieldbyname('Geschlecht').asstring;
      form2.Table1.fieldbyname('GEBDAT').asstring :=     table5.fieldbyname('gebdat').asstring;
      form2.Table1.fieldbyname('PUNKTE').asinteger :=    table5.fieldbyname('Punkte').asinteger;
    form2.table1.Post;
    table5.Next;
  end;
  showmessage('merge '+ table5.TableName + ' ok');
  table6.Close;
    table6.TableName := concat(home, '\''R5x.dbf');
  table6.active := true;
  table6.First;
  while not table6.Eof do
  begin
    form2.Table1.Append;
      form2.Table1.fieldbyname('NAME').asstring :=       table6.fieldbyname('NAME').asstring;
      form2.Table1.fieldbyname('VORNAME').asstring :=    table6.fieldbyname('VORNAME').asstring;
      form2.Table1.fieldbyname('KLASSE').asstring :=     table6.fieldbyname('KLASSE').asstring;
      form2.Table1.fieldbyname('GESCHLECHT').asstring := table6.fieldbyname('Geschlecht').asstring;
      form2.Table1.fieldbyname('GEBDAT').asstring :=     table6.fieldbyname('gebdat').asstring;
      form2.Table1.fieldbyname('PUNKTE').asinteger :=    table6.fieldbyname('Punkte').asinteger;
    form2.table1.Post;
    table6.Next;
  end;
  showmessage('merge '+ table6.TableName + ' ok');
  table7.Close;
    table7.TableName := concat(home, '\''R6x.dbf');
  table7.active := true;
  table7.First;
  while not table7.Eof do
  begin
    form2.Table1.Append;
      form2.Table1.fieldbyname('NAME').asstring :=       table7.fieldbyname('NAME').asstring;
      form2.Table1.fieldbyname('VORNAME').asstring :=    table7.fieldbyname('VORNAME').asstring;
      form2.Table1.fieldbyname('KLASSE').asstring :=     table7.fieldbyname('KLASSE').asstring;
      form2.Table1.fieldbyname('GESCHLECHT').asstring := table7.fieldbyname('Geschlecht').asstring;
      form2.Table1.fieldbyname('GEBDAT').asstring :=     table7.fieldbyname('gebdat').asstring;
      form2.Table1.fieldbyname('PUNKTE').asinteger :=    table7.fieldbyname('Punkte').asinteger;
    form2.table1.Post;
    table7.Next;
  end;
  showmessage('merge '+ table7.TableName + ' ok');
  table8.Close;
    table8.TableName := concat(home, '\''RDx.dbf');
  table8.active := true;
  table8.First;
  while not table8.Eof do
  begin
    form2.Table1.Append;
      form2.Table1.fieldbyname('NAME').asstring :=       table8.fieldbyname('NAME').asstring;
      form2.Table1.fieldbyname('VORNAME').asstring :=    table8.fieldbyname('VORNAME').asstring;
      form2.Table1.fieldbyname('KLASSE').asstring :=     table8.fieldbyname('KLASSE').asstring;
      form2.Table1.fieldbyname('GESCHLECHT').asstring := table8.fieldbyname('Geschlecht').asstring;
      form2.Table1.fieldbyname('GEBDAT').asstring :=     table8.fieldbyname('gebdat').asstring;
      form2.Table1.fieldbyname('PUNKTE').asinteger :=    table8.fieldbyname('Punkte').asinteger;
    form2.table1.Post;
    table8.Next;
  end;
  showmessage('merge '+ table8.TableName + ' ok');
  label21.Caption := inputstring;
  showmessage('MergeFile wurde erzeugt');
end;


Da bin ich mal gespannt auf eure Antworten,
Detlef


Niko S. - Di 07.07.09 20:16

sind diese tables sichtbare objekte? wenn ja mach die mal unsichtbar meistens ist das denn viel schneller..


Regan - Di 07.07.09 20:26

Kann es sein, dass das asstring sehr lange dauert? Ich habe zwar von dieser Datenbankart keine Ahnung, aber Strings umwandeln dauert ja an sich schon lange.


Sinspin - Di 07.07.09 20:27

user profile iconNiko S. hat folgendes geschrieben Zum zitierten Posting springen:
sind diese tables sichtbare objekte? wenn ja mach die mal unsichtbar meistens ist das denn viel schneller..

Joa, dem schließe ich mich an!
Man kann mit einem Aufruf Table.DisableControls dafür sorgen das eine Tabelle keine Ereignisse mehr von sich gibt wenn sich an ihr was ändert, also bei Datensatzwechsel oder so. Mit EnableControls musst du das dann aber wieder anschalten wenn du fertig bist.
Statt den Zweisungen via AsString kannst du es auch mal mit Value probieren. (FieldByName('xxx').Value := FieldByName('xxx').Value) So müssten sich eigentlich ein paar Sekunden über die gesammten Daten rausschlagen lassen.


jasocul - Mi 08.07.09 09:32

Über wieviel Datensätze reden wir denn hier?
Offensichtlich setzt du auch noch die BDE ein. Mach mal zwischendurch ein FlushBuffers. Wenn ich mich richtig erinnere, wird sonst alles "irgendwo" zwischengespeichert, bis die Datenbank genug Zeit hat, dass wirklich zu speichern.

Es gibt noch weiteres Optimierungspotential:
- AsString rausschmeißen.
- Statt FieldByName über Fields[FeldIndex] zugreifen


D. Annies - Mi 08.07.09 16:09

Danke für eure Ideen.

Aber da es nur ca. 900 DS waren, konnten diese Tuningmöglichkeiten nicht so viel bringen. Ich habe ganz "einfach" eine neue Table genommen, und dann ging's, also, in der obigen Lesart table3 statt table2.

Grüße aus Lübeck,
Detlef


Bergmann89 - Do 09.07.09 00:58

Hey,

nur ma so nebenbei, warum hast dir nich ne extra Funktion gemacht, die die daten kopiert. Weil du machst ja 7ma das gleiche un wenn du was ändern musst, änderst du das dann 7ma. das is irgendwie umständlich...

Mfg Bergmann.


D. Annies - Do 09.07.09 13:23

hast völlig recht, das mach ich als nächstes, eine schöne Proc mit Übergabeparameter.
Danke, Detlef


alzaimar - Do 09.07.09 20:14

Übrigens: "FieldByName" sucht jedesmal das entsprechende Feld aus der Feldliste (mit einfacher For-Schleife).
Bei 900 Records ist das nicht so wild, aber grundsätzlich sollte man vor so einer Schleife seine Felder einmalig suchen und in einer lokalen 'TField'-Variablen speichern. Dann geht das nochmals schneller.


D. Annies - Do 09.07.09 20:36

Hm, alzi, hast du mal ein Beispiel?
(vielleicht adaptiert von oben)
Detlef


alzaimar - Fr 10.07.09 08:23

user profile iconD. Annies hat folgendes geschrieben Zum zitierten Posting springen:
Hm, alzi, hast du mal ein Beispiel?

Klar, Deti :lol:

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:
25:
26:
27:
28:
29:
procedure TForm1.Button28Click(Sender: TObject);
var  
  InputString : string;
  fld1Name, fld1Vorname, fld1Klasse, fld1Geschlecht,  fld1Datum, fld1Punkte,
  fld2Name, fld2Vorname, fld2Klasse, fld2Geschlecht,  fld2Datum, fld2Punkte : TField;

begin
  ...
  table1.active := true;
  table1.First;
  
  fld1Name := form2.Table1.fieldbyname('NAME');
  fld1Vorname := form2.Table1.fieldbyname('VORNAME');
  ...
  fld2Name := Table2.fieldbyname('NAME');
  fld2Vorname := Table2.fieldbyname('VORNAME');
  ...
  while not table1.Eof do
  begin
    form2.Table1.Append;
    fld1Name.Value := fld2Name.Value;
    fld1Vorname.Value := fld2Vorname.Value;
    ...
    form2.table1.Post;
    table1.Next;
  end;
  showmessage('merge '+ table1.TableName + ' ok');
...
end;

Oder gleich noch kompakter:

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:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
Function CreateStringListFromCommaString (aCommaText : String) : TStringList;
Begin
  Result := TStringList.Create;
  Result.Delimiter := ',';
  Result.DelimitedText := aFieldNames;
End;

Procedure CopyTables (aTblName : String; aSrc, aDst : TTable, aFieldnames : String);
Var
  srcFields, dstFields : Array Of TField;
  i, fieldCount : Integer;
  slFieldNames : TStringList;

Begin
  aSrc.Close;
  aSrc.TableName := concat(home, '\', aTblName);
  aSrc.active := true;
  slFieldNames := CreateStringListFromCommaString(aFieldNames);
  Try
    setLength (srcFields, slFieldNames.Count);
    setLength (dstFields, slFieldNames.Count);
    fieldCount := Length (Fieldnames);
    For i := 0 to FieldCount - 1 do begin
      srcFields[i] := aSrc.FieldByname(slFieldnames[i]);
      dstFields[i] := aDst.FieldByname(slFieldnames[i]);
    End;
  Finally
    slFieldNames.Free;
  End;
  aSrc.First;
  while not aSrc.Eof do Begin
    aDst.Append;
    For i := 0 do FieldCount - 1 do
      dstFields[i].Value := SrcFields[i].Value;
    aDst.Post;
    aSrc.Next;
  End;
  showmessage('merge '+ aSrc.TableName + ' ok');
End;

procedure TForm1.Button28Click(Sender: TObject);
var
  InputString : string;

begin
  CopyTables ('R1x.dbf',table1, form2.Table1, 'Name,Vorname,Klasse,Geschlecht,GebDat,Punkte');
  CopyTables ('R7x.dbf',table3, form2.Table1, 'Name,Vorname,Klasse,Geschlecht,GebDat,Punkte');
  CopyTables ('R8x.dbf',table4, form2.Table1, 'Name,Vorname,Klasse,Geschlecht,GebDat,Punkte');
  CopyTables ('R9x.dbf',table5, form2.Table1, 'Name,Vorname,Klasse,Geschlecht,GebDat,Punkte');
  CopyTables ('R5x.dbf',table6, form2.Table1, 'Name,Vorname,Klasse,Geschlecht,GebDat,Punkte');
  CopyTables ('R6x.dbf',table7, form2.Table1, 'Name,Vorname,Klasse,Geschlecht,GebDat,Punkte');
  CopyTables ('Rdx.dbf',table8, form2.Table1, 'Name,Vorname,Klasse,Geschlecht,GebDat,Punkte');
  showmessage('MergeFile wurde erzeugt');
end;

Ungetestet.


D. Annies - Fr 10.07.09 19:41

Hi, alzi,
Donnerwetter! Da kann ich was mit anfangen!

Einige Kleinigkeiten habe ich schon beseitigt.
Noch stört die Fehlermeldung: Undefinierter Bezeichner 'afieldnames'

Delphi-Quelltext
1:
Result.DelimitedText := aFieldNames;                    


Bis wann?
Deti :D :D :D


alzaimar - Fr 10.07.09 20:05

Oh, sch**** Copy & Paste.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
Function CreateStringListFromCommaString (aCommaText : String) : TStringList;
Begin
  Result := TStringList.Create;
  Result.Delimiter := ',';
  Result.DelimitedText := aCommaText;
End;


D. Annies - Fr 10.07.09 20:15

Wow, danke für deine prompte Antwort.


D. Annies - Fr 10.07.09 20:24

Einen "kleinen" hab ich noch zur Laufzeit:

Listenindex überschreitet das Maximum(6)

Wasndas?


alzaimar - Fr 10.07.09 20:56

user profile iconD. Annies hat folgendes geschrieben Zum zitierten Posting springen:
Listenindex überschreitet das Maximum(6)
Wasndas?

Das nennt sich 'Runtime-Error' bzw. Laufzeitfehler. Wieso fragst Du?

Ach, liegt an dem Müll, den ich zusammengetippselt habe...

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:
25:
26:
27:
28:
29:
30:
31:
32:
Procedure CopyTables (aTblName : String; aSrc, aDst : TTable; aFieldnames : String);
Var
  srcFields, dstFields : Array Of TField;
  i : Integer;
  slFieldNames : TStringList;

Begin
  aSrc.Close;
  aSrc.TableName := concat(home, '\', aTblName);
  aSrc.active := true;
  slFieldNames := CreateStringListFromCommaString(aFieldNames);
  Try
    setLength (srcFields, slFieldNames.Count);
    setLength (dstFields, slFieldNames.Count);

    For i := 0 to slFieldNames.Count - 1 do begin
      srcFields[i] := aSrc.FieldByname(slFieldnames[i]);
      dstFields[i] := aDst.FieldByname(slFieldnames[i]);
    End;
  Finally
    slFieldNames.Free;
  End;
  aSrc.First;
  while not aSrc.Eof do Begin
    aDst.Append;
    For i := 0 do slFieldNames.Count - 1 do
      dstFields[i].Value := SrcFields[i].Value;
    aDst.Post;
    aSrc.Next;
  End;
  showmessage('merge '+ aSrc.TableName + ' ok');
End;

Und nu?


D. Annies - Fr 10.07.09 21:55

(klar kenne ich solche Runtime-Errors, aber ich wusste nicht, wo DER herkommt)

Einen hab ich noch:

zur Laufzeit: EConvertError Format '%p' ungültig oder nicht kompatibel mit Argument.
Ist der auch noch von deinem "Geschnipsel"? :P

Gruß, Deti


D. Annies - Fr 10.07.09 22:03

Ups, ich meine, ich habe es gesehen: dann muss wohl auch 'Punkte' Integer sein, wa?

Nein, gleicher Fehler - aber es sind ja Integer's in den Tabellen, also doch diese Ecke?


alzaimar - Fr 10.07.09 23:24

user profile iconD. Annies hat folgendes geschrieben Zum zitierten Posting springen:
Format '%p' ungültig oder nicht kompatibel mit Argument. Ist der auch noch von deinem "Geschnipsel"?
Siehst Du '%p' in meinem Cöde?


D. Annies - Sa 11.07.09 07:59

Nö, also in meinem Code aber auch nicht. Ich habe jetzt zwei showmessage('hier bin ich') eingebaut, nach den beiden setlength-Anweisungen und nach slFieldNames.free.

Beide Stellen werden noch fehlerfrei erreicht, aber dann kommt die (eine neue) Fehlermeldung: Privilegierte Anweisung

Detlef


D. Annies - Sa 11.07.09 10:01

Habe jetzt weiter eingegrenzt:

For i := 0 to 5 do // slFieldNames.Count - 1 do

wenn ich also slFieldnames.count-1 durch die Zahl 5 ersetze (weil es 6 Felder sind), dann tritt keine Fehlermeldung mehr auf, aber es werden auch keine Daten geschrieben.

Seltsam, seltsam,
Detlef


alzaimar - Sa 11.07.09 10:03

Wie hoch ist den slFieldNames.Count? Wird vielleicht ein leerer Eintrag erzeugt?

Merke: In der Softwareentwicklung ist nichts 'seltsam', sondern immer erklärbar.


D. Annies - Sa 11.07.09 10:15

So, nun klappt es:


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:
25:
26:
27:
28:
29:
30:
31:
Procedure TForm1.CopyTables (aTblName : String; aSrc, aDst : TTable; aFieldnames : String);
Var  srcFields, dstFields : Array Of TField;
     i                    : Smallint;
     slFieldNames         : TStringList;
Begin
  aSrc.Close;
    aSrc.TableName := concat(home, '\', aTblName);
  aSrc.active := true;
  slFieldNames := CreateStringListFromCommaString(aFieldNames);
  Try
    setLength(srcFields, slFieldNames.Count);
    setLength(dstFields, slFieldNames.Count);
    For i := 0 to slFieldNames.Count - 1 do
    begin
      srcFields[i] := aSrc.FieldByname(slFieldnames[i]);
      dstFields[i] := aDst.FieldByname(slFieldnames[i]);
    End;
    aSrc.First;
    while not aSrc.Eof do
    Begin
      aDst.Append;
      For i := 0 to slFieldNames.Count - 1 do
        DstFields[i].Value := SrcFields[i].Value;
      aDst.Post;
      aSrc.Next;
    End;
    showmessage('merge '+ aSrc.TableName + ' ok');
  Finally
    slFieldNames.Free;
  End;
End;


Danke für deine Mühe, "alzi"
Gruß aus Lübeck von "Deti"


kkausp - Sa 11.07.09 10:30

Hallo,

Es gibt für die BDE die Delphi Komponente TBATCHMOVE, welche auch das kopieren von Datensätzen ermöglicht.


alzaimar - Sa 11.07.09 14:12

Nur sollte man die BDE in die Tonne treten. Leider, denn daran hatte ich zwischendurch auch mal gedacht.