Einzelnen Beitrag anzeigen

NickelM

Registriert seit: 22. Jul 2007
Ort: Carlsberg
445 Beiträge
 
Delphi 2009 Professional
 
#1

Datenbank auf Streambasis

  Alt 3. Mär 2012, 20: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.
  Mit Zitat antworten Zitat