Registriert seit: 22. Jul 2007
Ort: Carlsberg
445 Beiträge
Delphi 2009 Professional
|
Datenbank auf Streambasis
3. Mär 2012, 21:28
Hallo zusammen,
Ich habe mir ein kleines Datenbanksystem auf Stream-Basis sozusagen gebaut. Beim ändern eines Datensatzes habe ich bedenken, dass dies so optimal ist, wegen der Schnelligkeit. zurzeit hab ich es noch nicht mit vielen Datensätzen getestet, aber vielleicht fällt jemanden etwas auf, was man verbessern könnte. Ich weis es ist viel Code, hoffe ich habe es gut erklärt.
Delphi-Quellcode:
type
TArtikelFileRec = packed record
ArtikelNr : Cardinal;
Stückpreis : Cardinal;
SLength : Cardinal;
end;
TArtikel = record
DateiPos : Int64;
ArtikelInfo : TArtikelFileRec;
end;
PArtikel = ^TArtikel;
//Prozedur
var Artikel : PArtikel;
Artikelname : AnsiString;
NewSLength,SLength,I : Integer;
NewSize : Integer;
DelSize : Integer;
CopySize : Integer;
Size : Integer;
Offset : Pointer;
SizeFileRec : Integer;
Item : TListItem;
FileHandle : Integer;
OldDateiPos : Int64;
begin
Item := ListView1.Selected;
if Item <> nil then
begin
frmArtkEdit.Caption := 'Artikel ändern';
Artikel := Item.Data;
with Artikel^ do
begin
frmArtkEdit.edtArtnr.Text := IntToStr(ArtikelInfo.ArtikelNr);
frmArtkEdit.edtArtName.Text := ListView1.Selected.SubItems[0];
frmArtkEdit.edtpreis.Text := CurrToStr(ArtikelInfo.Stückpreis / 100); //Preis ohne Komma
end;
frmArtkEdit.ShowModal;
If frmArtkEdit.ModalResult = mrOk then
begin
with Artikel^ do
begin
SizeFileRec := SizeOf(TArtikelFileRec);
Artikelname := frmArtkEdit.edtArtName.Text;
NewSLength := Length(Artikelname); //Neue Stringlänge
SLength := ArtikelInfo.SLength; //Alte Stringlänge
if NewSLength = SLength then //Wenn der String gleichlang ist kann ich direkt die alten Daten überschreiben
begin
ArtikelInfo.ArtikelNr := StrToInt(frmArtkEdit.edtArtnr.Text);
ArtikelInfo.SLength := NewSLength;
ArtikelInfo.Stückpreis := Trunc(StrToCurr(frmArtkEdit.edtpreis.Text) * 100);
//ArtikelFile ist ein TMemoryStream, wird beim Start initalisiert. Lade dort die "Datenbank" in den Speicher. Warum ich das mache ich weiter unten.
NewSize := ArtikelFile.Size;
ArtikelFile.Position := DateiPos; //DateiPos ist die Pos des Datensatzes in der Datei, den ich mir beim Auslesen durch ArtikelFile.Position vor jedem Datensatzes hole.
ArtikelFile.Write(ArtikelInfo,SizeOf(ArtikelInfo)); //Daten in
ArtikelFile.Write(Artikelname[1],NewSLength); //Stream ändern
FileHandle := FileOpen(ExePfad + 'artikel.list',fmOpenWrite); //Läd Windows eigentlich beim öffnen, die Datei in den Speicher? Ja oder?
FileSeek(FileHandle,DateiPos,0); //Zum Datensatz anfang in Datei springen
FileWrite(FileHandle, Pointer(Longint(ArtikelFile.Memory) + DateiPos)^,SizeOf(ArtikelInfo)+ NewSLength); //Mit dem Pointer von MemoryStream, den geänderten Datensatz in die Datei schreiben (geht schneller als direkt wo ich oben die Daten in dem Stream geschrieben hab oder?
FileClose(FileHandle); //Datei wieder schliessen, brauch ich ja nicht, hab ja die Datei in einem Stream.
Item.Caption := IntToStr(ArtikelInfo.ArtikelNr);
Item.SubItems[0] := Artikelname;
Item.SubItems[1] := Format('%m',[ArtikelInfo.Stückpreis/100]);
end
else
begin //Falls die Texte unterschiedlich lang sind, muss ich die Datei anpassen, genau um diesen Teil geht es.
ArtikelInfo.ArtikelNr := StrToInt(frmArtkEdit.edtArtnr.Text);
ArtikelInfo.SLength := NewSLength; //Neue Textlänge
ArtikelInfo.Stückpreis := Trunc(StrToCurr(frmArtkEdit.edtpreis.Text) * 100);
Size := ArtikelFile.Size; //Akuelle Größe der Datei/Speicherabbild
NewSize := Size + (NewSLength - SLength); //Die neue Größe gerechnen, SLength ist oben schon gesetzt
CopySize := Size - DateiPos - SizeFileRec - SLength; //Sind Datensätze nach dem Aktuellen Datensatz? Wenn ja, wie viele Bytes sind das..
if CopySize > 0 then //Ja es gibt welche...
begin
GetMem(Offset,CopySize); //Einen "Übergangsspeicher", muss ja irgendwo die Daten sichern. Wenn der neue Text länger ist als der alte, würde ich ihn ja überschreiben
Move(Pointer(LongInt(ArtikelFile.Memory) + DateiPos +
SizeFileRec + SLength)^,Offset^,CopySize); //Deshalb habe ich ein Speicherabbild, geht doch schneller als die Daten direkt von Datei zulesen oder?
ArtikelFile.SetSize(NewSize); //Stream an neue größe anpassen, ist ja egal ob nun was wegfällt, habs ja gesichert sozusagen
ArtikelFile.Position := DateiPos;
ArtikelFile.Write(ArtikelInfo,SizeOf(ArtikelInfo));
ArtikelFile.Write(Artikelname[1],NewSLength);
Move(Offset^,Pointer(LongInt(ArtikelFile.Memory) + DateiPos + SizeOf(ArtikelInfo) + NewSLength)^,CopySize);
FreeMem(Offset,CopySize);
for I := Item.Index+1 to ListView1.Items.Count - 1 do //Nur "interne" Liste anpassen, da sich ja zwangsläufig alle Datensätze, die Positionen in der Datei geändert haben. nur Differenz anpassen.
with PArtikel(ListView1.Items[I].Data)^ do
DateiPos := DateiPos + (NewSLength - SLength);
end
else
begin //Wenn ich nicht zukopieren gibt, da der Datensatz der letze ist, einfach überschreiben
ArtikelFile.Position := DateiPos;
ArtikelFile.Write(ArtikelInfo,SizeOf(ArtikelInfo));
ArtikelFile.Write(Artikelname[1],NewSLength);
end;
Item.Caption := IntToStr(ArtikelInfo.ArtikelNr);
FileHandle := FileOpen(ExePfad + 'artikel.list',fmOpenWrite);
FileSeek(FileHandle,DateiPos,0);
FileWrite(FileHandle, Pointer(LongInt(ArtikelFile.Memory) + DateiPos)^,SizeOf(ArtikelInfo) + NewSLength + CopySize); //Daten in die Datei kopieren,
SetEndOfFile(FileHandle); //Da ich ja dan die Datei aufhört, weil ich egal ob ich kopieren muss oder nicht am Ende bin, Dateigröße anpassen.
FileClose(FileHandle);
Item.SubItems[0] := Artikelname;
Item.SubItems[1] := Format('%m',[ArtikelInfo.Stückpreis/100]);
end;
end;
ShowMessage('Artikel erfolgreich geändert');
end;
end;
Soweit zur Erklärung meines Codes. Ich genutze deshalb ein Speicherabbild der Datei, da ich der Meinung bin, es geht schneller, Daten im Speicher zu kopieren/verschieben, als Daten von der Datei in den Speicher zukopieren und dan wieder in die Datei zuschreiben. Oder irre ich da?
Gruß
NickelM
Nickel "Lebe und denke nicht an morgen"
Zitat aus dem gleichnamigen Bollywoodfilm.
|