AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

Datenbank auf Streambasis

Ein Thema von NickelM · begonnen am 3. Mär 2012 · letzter Beitrag vom 5. Mär 2012
Antwort Antwort
NickelM

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

Datenbank auf Streambasis

  Alt 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.
  Mit Zitat antworten Zitat
Benutzerbild von sx2008
sx2008

Registriert seit: 16. Feb 2008
Ort: Baden-Württemberg
2.332 Beiträge
 
Delphi 2007 Professional
 
#2

AW: Datenbank auf Streambasis

  Alt 3. Mär 2012, 21:45
Also das ist kein "Datenbanksystem" sondern nur etwas unstrukturierter Code mit dem man mehrere Records mit festgelegter Struktur in einer Datei speichern kann.
Das Speichern von Daten hat sich ja im Laufe der Zeit weiterentwickelt, aber dieser Code steht noch bei Phase 0.

Phase 0: Daten mit fester Satzstruktur werden einfach in eine Datei gespeichert
Phase 1: Die Datei bekommt einen Header, anhand dessen man die Version abfragen kann
Phase 2: Der Header wird erweitert um eine Liste mit Feldbeschreibungen (Feldname, Länge, Datentyp, Genauigkeit, Defaultwert)
Phase 3: Jeder Datensatz bekommt ein Löschkennzeichen, damit nicht bei jedem Löschen Megebytes an Daten kopiert werden müssen.
Phase 4: Die Software und das Datenformat wird standardisiert

Was ich hier mit Phase 0 bis 4 beschrieben habe sind die Minimalanforderungen die zum steinalten dBase-Format geführt haben.
  Mit Zitat antworten Zitat
NickelM

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

AW: Datenbank auf Streambasis

  Alt 3. Mär 2012, 21:56
Naja okay, ich gebe dir Recht. Es ging mir jetzt weniger darum dieses System zu verallgemeinern, also eine allgemeine Datenbank-Struktur zubauen.
Dieses System wollte ich nur verwenden, wenn ich nicht grad eine ganze Datenbank-Komponente oder irgendwelche andere Komponenten brauch um "Datensätze" (also einen Record oder anzahl von Bytes, die sich immer wiederholen. Halt eine Art Liste von Daten, mehr nicht.

Was aber an deinen Punkten interessant ist ist Phase 3.
Wie meinst du das? Wie würde das in einer Art Pseudo-Code aussehen?
Nickel
"Lebe und denke nicht an morgen"
Zitat aus dem gleichnamigen Bollywoodfilm.
  Mit Zitat antworten Zitat
Benutzerbild von sx2008
sx2008

Registriert seit: 16. Feb 2008
Ort: Baden-Württemberg
2.332 Beiträge
 
Delphi 2007 Professional
 
#4

AW: Datenbank auf Streambasis

  Alt 3. Mär 2012, 22:12
Jeder Datensatz bekommt ein Löschkennzeichen ganz am Anfang vor den eigentlichen Nutzdaten.
' ' bedeutet Daten sind gültig; '*' bedeutet Datensatz wurde gelöscht.
Die gelöschten Datensätze bleiben an Ort und Stelle in der Datei erhalten.
Man benötigt dann noch eine Restrukturierungsfunktion, die die Datei quasi defragmentiert.
Man kann z.B. alle Datensätze die nicht gelöscht sind in eine neue Datei kopieren.
Es ist auch möglich die Datensätze in der Orginaldatei zusammenzuschieben und zum Schluss die Datei zu verkürzen.
  Mit Zitat antworten Zitat
NickelM

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

AW: Datenbank auf Streambasis

  Alt 3. Mär 2012, 22:40
Es ist auch möglich die Datensätze in der Orginaldatei zusammenzuschieben und zum Schluss die Datei zu verkürzen.
Naja dies mache ich ja eigentlich. Aber ich schieb die Datei direkt nach jedem Löschen zusammen, um sicherzustellen, dass alles übernohmen wird. Weil dies System soll eine Art Live-"Datenbank" sein.
Jede Änderung, ob neuer Datensatz, Änderungen, Löschungen. Dies sollte der Sinn und Zweck dieses Systems sein. Aber vielleicht mache ich auch mal ein allgemeines System.

nunja zurück zum Thema. Könnte man noch irgendwelche Vorgänge optimieren, was z.b. Datei als Speicherabbild, und zum ändern Datei öffnen, und Daten ändern, wieder Datei schliessen betrifft.
Oder ist dies schon so optimal? Ich weis das neuere Rechner ja schnell sind, es geht halt auch darum, wenn ich mal das System verallgemeiner als "richtiges" Datenbank-System, ob dies dann auch noch so optimal ist.

Hört sich doff an, aber es geht sozusagen um die Optimierung die Datei auf den aktuellsten Stand zuhalten und trotzdem den Speicher/Arbeitsspeicher/Prozessor nicht zusehr auszulasten. Ich gehe da von Rechnern aus mit z.b. 500 Mhz, ca. 300 MB-RAM. Oder denk ich da "zu optimal"?
Nickel
"Lebe und denke nicht an morgen"
Zitat aus dem gleichnamigen Bollywoodfilm.
  Mit Zitat antworten Zitat
Furtbichler
(Gast)

n/a Beiträge
 
#6

AW: Datenbank auf Streambasis

  Alt 4. Mär 2012, 10:20
Jeder Datensatz bekommt ein Löschkennzeichen ganz am Anfang vor den eigentlichen Nutzdaten.
' ' bedeutet Daten sind gültig; '*' bedeutet Datensatz wurde gelöscht.
Die gelöschten Datensätze bleiben an Ort und Stelle in der Datei erhalten.
Man benötigt dann noch eine Restrukturierungsfunktion...
Das Löschkennzeichen kann man auch erweitern, indem man die gelöschten Records wie in einer linked List miteinander verknüpft: Steht eine 0 drin, ist der Record in Verwendung, jede andere Zahl bezeichnet die Recordnummer des nächsten gelöschten Records.

Wenn man nun den Index des ersten gelöschten Records kennt (den man z.B. im Dateiheader abgelegt hat), dann kann man die Operation "Gib neuen Recordindex" sehr einfach und schnell implementieren. Das Löschen eines Records würde zudem bedeuten, das der Record einfach vorne in die verkettete Liste eingefügt wird.

Auf diese Weise degeneriert so eine Datei im laufenden Betrieb nicht.

Dessenungeachtet würde ich die Aufgabe "Speichern eines Array of Record" eher mit ORM realisieren.
  Mit Zitat antworten Zitat
Panthrax

Registriert seit: 18. Feb 2005
286 Beiträge
 
Delphi 2010 Enterprise
 
#7

AW: Datenbank auf Streambasis

  Alt 5. Mär 2012, 15:51
Dieser Ansatz?
Delphi-Quellcode:
type
  TMeinRecord = record
  public
  type
    TDateidaten = record
      { ... }
    end;

  public
    Datei: TDateidaten;
    { ... }
  end;

  TMeineListe = class(TList<TMeinRecord>)
  public
    procedure LoadFromFile(const Filename: String);
    procedure LoadFromStream(const Stream: TStream);
    procedure SaveToFile(const Filename: String);
    procedure SaveToStream(const Stream: TStream);
  end;
"Es gibt keine schlimmere Lüge als die Wahrheit, die von denen, die sie hören, missverstanden wird."
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 17:05 Uhr.
Powered by vBulletin® Copyright ©2000 - 2022, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2021 by Daniel R. Wolf