![]() |
Datenbank auf Streambasis
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:
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?
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; Gruß NickelM |
AW: Datenbank auf Streambasis
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. |
AW: Datenbank auf Streambasis
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? |
AW: Datenbank auf Streambasis
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. |
AW: Datenbank auf Streambasis
Zitat:
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"? |
AW: Datenbank auf Streambasis
Zitat:
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. |
AW: Datenbank auf Streambasis
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; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:44 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz