Office Forum
www.Office-Loesung.de
Access :: Excel :: Outlook :: PowerPoint :: Word :: Office :: Wieder Online ---> provisorisches Office Forum <-
Minidatenbank, Type-Definitionen und Objekte speichern
zurück: Comboboxen dynamisch mit Wertelisten aus Tabelle befüllen weiter: Mehrwertige Felder mit echten m:n-Tabellen Unbeantwortete Beiträge anzeigen
Neues Thema eröffnen   Neue Antwort erstellen     Status: Tutorial Facebook-Likes Diese Seite Freunden empfehlen
Zu Browser-Favoriten hinzufügen
Autor Nachricht
Bitsqueezer
Office-VBA-Programmierer


Verfasst am:
28. Nov 2010, 01:37
Rufname:

Minidatenbank, Type-Definitionen und Objekte speichern - Minidatenbank, Type-Definitionen und Objekte speichern

Nach oben
       Version: (keine Angabe möglich)

Hallo zusammen,

hier mal ein Blick zurück in die Geschichte der Datenbanken, als es noch kein Access, keinen SQL Server oder andere relationale Datenbanksysteme auf Rechnern für Heimanwender gab.

Ja, diese Methode funktionierte sogar schon auf der guten alten 1541 vom C64, wenn auch nicht ganz so komfortabel zu programmieren wie heute unter VBA.

Wer mit alten BASIC-Dialekten programmiert hat, erinnert sich vielleicht an die schon beinahe vergessene Methode, eine Datei "wahlfrei" zu öffnen, wie das mal genannt wurde. Im Original "For Random".
Damit kann man eine binäre Datei öffnen, die dann wie eine kleine Datenbank gehandhabt wird. Das Prinzip der ersten relationalen Datenbanken, um schnell einen Datensatz addressieren zu können (als ein C64 noch 1 MHz (in Worten: MEGAHertz, nicht Gigahertz) hatte...) war, jedem Datensatz die exakt gleiche Länge zu geben, wodurch sich die Position jedes Datensatzes einfach berechnen läßt - ist ein Datensatz 20 Bytes lang, kann man einfach den dritten Datensatz bei Byte 60 der Datei wiederfinden. Das ist auch schon im groben und ganzen das ganze "Geheimnis" der "wahlfreien" Dateien, die feste Satzlänge.
Wenn man nun diese "Datenbank" beschreiben und lesen wollte, dann war die ID die Datensatznummer und schon konnte man den benötigten Datensatz aus der "Datenbank" zupfen - das geht in VBA heute immer noch so.

Aber warum sollte man in Zeiten relationaler Hochgeschwindigkeitsdatenbanken noch auf so ein Relikt zurückgreifen? Ganz einfach: Weil man damit auch heute noch ein paar Dinge machen kann, die man anders nur viel schwieriger lösen kann.

Beispiele:
  1. Speichern von Daten von Objekten, die aus Klassenmodulen erstellt wurden, als Datei
  2. Weitergabe von Objektdaten z.B. per EMail an eine andere, gleichartige Datenbank
  3. Verhindern, daß ein User manuell in die Datei eingreift
  4. Saubere Speicherung aller Arten von Datentypen mit extrem wenig Aufwand
  5. Keine Probleme mit Trennzeichen wie bei CSV-Dateien
  6. Keine Probleme mit Datensatzlängen wie bei CSV-Dateien
Wenn man z.B. ein simples Datenklassenmodul erstellt, das so aussieht:
Code:
Option Compare Database
Option Explicit

Public ID As Long
Public Textfield As String
Public Datefield As Date
Public Doublefield As Double
und es als "clsData" speichert, dann kann man hier bereits wunderbar Daten verschiedener Datentypen speichern.

Hat man nun eine ganze Collection mit Objekten dieser Klasse gefüllt und möchte diese Collection für spätere Zwecke wie den oben beschriebenen speichern, kann man sich nun mit den Random-Dateien behelfen. Wermutstropfen hierbei ist, daß man dazu die Datentypen des Objektes in einem Type-Objekt nachbilden muß, da der Open-Befehl mit eigenen Objekten nicht umgehen kann. Also schreibt man in z.B. ein Standardmodul folgendes:
Code:
Private Type MyType
    ID As Long
    Textfield As String * 30
    Datefield As Date
    Doublefield As Double
End Type
Diese Typdeklaration unterscheidet sich nur in einem Punkt von der Objektdefinition: Man kann hier bei Strings die Länge des Strings fest definieren, hier mit 30 Zeichen. Genaugenommen muß man das sogar, denn wie oben erklärt, benötigt die "Minidatenbank" ja eine feste Satzlänge, wohingegen der normale Strings bis zu 2GB Daten mit unterschiedlicher Länge aufnehmen kann.
Im Regelfall wird man aber die Maximallänge eines Strings kennen, die man im Objekt speichert, so daß man diese hier angeben kann.

Die Felder sind beliebig ausgewählt, es könnten natürlich auch ganz andere sein.

Mit dieser Vorarbeit kann man nun eine Save-Funktion in das Standardmodul schreiben:
Code:
Public Sub SaveObject()
    Dim objData As clsData
    Dim intFile As Integer
    Dim i As Long
    Dim TypeConvert As MyType
   
    Set ObjList = New Collection
    ' Zufällige Liste von 100 Datensätzen erzeugen
    For i = 1 To 100
        Set objData = New clsData
        With objData
            .ID = Rnd() * 100 + 1
            .Textfield = Left("Test " & .ID, 20)
            .Datefield = DateSerial(Rnd() * 2010, Rnd() * 12 + 1, Rnd() * 28 + 1)
            .Doublefield = Rnd()
        End With
        ObjList.Add objData
    Next i
    intFile = FreeFile
    Open "Test.obj" For Random As #intFile Len = Len(TypeConvert)
    For Each objData In ObjList
        With TypeConvert
            .ID = objData.ID
            .Textfield = objData.Textfield
            .Datefield = objData.Datefield
            .Doublefield = objData.Doublefield
            Debug.Print .ID, .Textfield, .Datefield, .Doublefield
        End With
        Put #intFile, , TypeConvert
    Next objData
    Close #intFile
End Sub
Der erste Teil dieser Funktion würde natürlich normalerweise nicht Bestandteil der Save-Funktion sein, er erzeugt zu Demonstrationszwecken eine Liste von 100 Objekten des Typs "clsData" mit zufälligen Werten.
Im Anschluß werden die Werte in die Collection "ObjList" hinzugefügt, die dann somit auch die Reihenfolge definiert. Der Einfachheit halber habe ich auf einen Key für die Collection verzichtet. Im Fall einer eindeutigen ID könnte man diese auch als Key benutzen.

Das Speichern beginnt mit der Zeile "intFile = FreeFile", die eine freie Kanalnummer sucht.
Danach wird eine "Minidatenbank" geöffnet oder, falls noch nicht existiert, eine erstellt. Das alles passiert mit diesem einen Befehl (Statt "Test.obj" kann man einen beliebigen Dateinamen nehmen und sollte normalerweise den Pfad hinzufügen). Der letzte Teil des Befehls bestimmt die Datensatzlänge, dieser mißt die Länge des selbstdefinierten Typs "MyType" anhand der deklarierten Variable "TypeConvert" (die natürlich ebenfalls beliebig anders heißen kann).

Die Variable "TypeConvert" dient hier nur als "Zwischenstation", sie übernimmt in der folgenden Schleife die Daten der Objekte objData, die aus der Collection ausgelesen werden. Mit dem "Put"-Befehl wird dann die Variable TypeConvert mit all ihren "Untervariablen" in einem Rutsch an die nächste Datensatzzeiger-Position geschrieben. Beim Öffnen ist das immer 1, überschreibt also jeglichen alten Inhalt, falls da schon was stand, oder fügt es an. Ein "INSERT" und "UPDATE" in einem Befehl. Einfacher geht's nicht.
Man muß lediglich berücksichtigen, daß ein früherer Schreibversuch vielleicht 200 Datensätze geschrieben hat, so daß man bei einem neuen Save mit nur 100 Datensätzen die letzten 100 alten Datensätze nicht überschreibt, man diese also weiterhin aus der Datei lesen kann. Im Zweifelsfall also einfach die Datei mit einem "Kill"-Befehl vorher löschen, wenn man immer genau die richtige Anzahl Datensätze zurückerhalten will - oder auch nicht, wenn man wirklich wie eine Datenbank damit arbeiten möchte.

Mit dieser kleinen Schleife hat man eine komplexe Datenstruktur säuberlich und übersichtlich und typrein in eine Binärdatei geschrieben, und man bekommt sie ebensoleicht wieder zurück:
Code:
Public Sub LoadObject()
    Dim objData As clsData
    Dim TypeConvert As MyType
    Dim intFile As Integer
   
    Set ObjList = New Collection
    intFile = FreeFile
    Open "Test.obj" For Random As #intFile Len = Len(TypeConvert)
    Do Until EOF(intFile)
        Set objData = New clsData
        Get #intFile, , TypeConvert
        With TypeConvert
            objData.ID = .ID
            objData.Textfield = .Textfield
            objData.Datefield = .Datefield
            objData.Doublefield = .Doublefield
        End With
        ObjList.Add objData
    Loop
    For Each objData In ObjList
        With objData
            Debug.Print .ID, .Textfield, .Datefield, .Doublefield
        End With
    Next objData
    Close #intFile
End Sub
Die Schleife am Schluß ist natürlich nicht notwendig, dient nur der Ausgabe im Direktfenster.
Der Weg geht hier genau umgekehrt, man lädt den jeweils nächsten Datensatz mit "Get" in die Typvariable "TypeConvert" und schreibt sie dann in ein neu erstelltes Objekt vom Typ "clsData" zurück und schiebt es im Anschluß in die Collection - Ergebnis ist, daß die Collection wieder exakt hergestellt ist, in der gleichen Reihenfolge, und mit fast 0 Aufwand.

Ein weiterer Vorteil ist, daß man mit Get und Put mit dem hier nicht verwendeten dritten Parameter auch noch eine Datensatznummer angeben kann, um einen ganz bestimmten Datensatz zu lesen oder zu schreiben. Wenn man also etwa als ID eine exakt sequentielle Nummernreihenfolge verwendet ("Löcher" wie beim AutoIncrement Feld sind hier natürlich nicht erlaubt, die beim Löschen eines Datensatzes in einer Tabelle mit AutoIncrement-PK entstehen), kann man auch ganz gezielt nur die benötigten Objekte aus so einer Datei herausholen, mit wahrlich sehr wenigen Befehlen, wie man hier sieht.

Das mit einer CSV-Datei zu machen, wäre sehr viel schwieriger und auch sehr viel langsamer. Bei CSV schreibt man ja einfach ein beliebiges Trennzeichen zwischen die Felder und kann so beliebig viele Felder aneinanderketten. Wenn da nicht die Zeilengrenze von 1024 Zeichen wäre, die einige Systeme als späteste Grenze einer Zeile erkennen, die nächste Zeile (oder nach einem CR oder CRLF) ist dann der nächste Datensatz. Längere Zeilen als 1024 gehen in vielen Systemen nicht mehr.
Nicht so hier: Man kann ohne weiteres längere Datensätze definieren, da die Länge hier nicht durch ein Zeichen begrenzt wird oder durch einen Zeilenpuffer, sondern einfach die Zeilenlänge in Bytes berechnet wird. Dadurch sind solche Dateien naturgemäß größer als CSV-Dateien, dafür aber auch schneller: Wenn ich Datensatz 20.000 aus einer solchen Datei lese, dann bekomme ich den SOFORT geliefert, da die Position in der Datei sofort bestimmt werden kann und der Dateipositionszeiger sofort auf die richtige Stelle positioniert wird - so funktionierten schnelle Datenbanken in den Anfängen.
Bei einer CSV-Datei müßte man nun jede Zeile nacheinander einlesen und deren Inhalt lesen, um die Zeile 20.000 zu finden.

Eine andere denkbare Idee für so eine Ausgabe in eine Random-Datei ist, den Inhalt einer Datenbanktabelle auf diese Weise schnell und komfortabel zu exportieren. Man muß sich keine Gedanken um ein Trennzeichen machen, das nicht in Textfeldern vorkommen darf, man kann einfach alles exportieren, was irgendwie in "normale" Datentypen paßt.

Eine so exportierte Tabelle kann natürlich auch für andere Zwecke benutzt werden: Zumindest die Microsoft-Basicdialekte können mindestens seit QBasic für DOS damit umgehen (ich weiß allerdings nicht genau, ob sie alle kompatibel sind), somit könnte man so eine Tabelle ebenso in einem ASP-Skript in einem Webshop als Minidatenbank einsetzen, das kein Datenbank-Backend benötigt (naja, das wäre etwas extrem..Smile) wie auch in einem VBScript, das vielleicht die Benutzerordner auf einem Fileserver anhand einer Usertabelle anlegen soll, ohne Zugriff auf die Datenbank haben zu müssen, die selbst vielleicht in einer anderen Lokation verwaltet wird.
Da es eine Art Datenbank ist, kann das VBScript sogar Filter verwenden, um nur einen Auszug zu verwenden usw.

Es gibt sehr viele Möglichkeiten, was man damit machen kann. Klar sollte allerdings auch sein, daß diese Methode nicht die CSV-Dateien verdrängen soll und auch nicht kann, denn während wahrscheinlich 100% aller Datenbanken und vieler weiterer Systeme CSV-Dateien einlesen und verarbeiten können, gibt es nur wenige Programme, die die Minidatenbanken verwenden können.

Man könnte sich übrigens noch die Frage stellen, wozu überhaupt ein Objekt verwenden, wenn doch der selbstdefinierte Type das gleiche erledigen kann. Berechtigte Frage, vielfach muß man das im Prinzip auch nicht, da man auch die Types anderweitig verwenden kann. Allerdings haben die Types einen gravierenden Nachteil: Man kann sie beispielsweise nicht als Parameter verwenden, und auch an anderen Stellen hakt es mit Types, wohingegen man mit einem Objekt fast alles machen kann, inklusive dem Hinzufügen von Methoden, was mit Types natürlich nicht geht, oder Properties, die mehr tun können als nur einfache Daten zu schaufeln.
Umgekehrt kann man ein Objekt leider nicht im Open-Befehl angeben, so daß hier der Schritt über die Type-Variable zur Konvertierung notwendig ist.

Viel Spaß beim Experimentieren

Christian

PS: Das Verfahren sollte in jeder Access-Version lauffähig sein, ebenso kann man auf diese Weise aber auch in anderen VBA-Dialekten wie Excel oder Powerpoint verfahren.
Willi Wipp
Moderator


Verfasst am:
09. Apr 2011, 07:27
Rufname:
Wohnort: Raum Wiesbaden


AW: Minidatenbank, Type-Definitionen und Objekte speichern - AW: Minidatenbank, Type-Definitionen und Objekte speichern

Nach oben
       Version: (keine Angabe möglich)

{Dieser Beitrag nimmt das Thema aus den unbeantworteten Themen heraus}
Neues Thema eröffnen   Neue Antwort erstellen Alle Zeiten sind
GMT + 1 Stunde

Diese Seite Freunden empfehlen

Seite 1 von 1
Gehe zu:  
Du kannst Beiträge in dieses Forum schreiben.
Du kannst auf Beiträge in diesem Forum antworten.
Du kannst deine Beiträge in diesem Forum nicht bearbeiten.
Du kannst deine Beiträge in diesem Forum nicht löschen.
Du kannst an Umfragen in diesem Forum nicht mitmachen.
Du kannst Dateien in diesem Forum nicht posten
Du kannst Dateien in diesem Forum herunterladen

Verwandte Themen
Forum / Themen   Antworten   Autor   Aufrufe   Letzter Beitrag 
Keine neuen Beiträge Access Tabellen & Abfragen: Datensätze von Haupttabelle in Untertabellen speichern 0 Schriftführer 592 24. Aug 2007, 11:22
Schriftführer Datensätze von Haupttabelle in Untertabellen speichern
Keine neuen Beiträge Access Tabellen & Abfragen: Mit SQL Daten speichern 4 freichle 904 30. Jul 2007, 09:55
freichle Mit SQL Daten speichern
Keine neuen Beiträge Access Tabellen & Abfragen: NULL in Tabelle speichern 6 Christian E. 593 03. Mai 2007, 15:12
Christian E. NULL in Tabelle speichern
Keine neuen Beiträge Access Tabellen & Abfragen: berechnendes Feld speichern / Aktualisierungsabfrage 6 BLBGünter 1085 27. März 2007, 21:39
Nouba berechnendes Feld speichern / Aktualisierungsabfrage
Keine neuen Beiträge Access Tabellen & Abfragen: Abfrageergebniss in einer Tabelle speichern 3 Marsfrau 608 24. März 2007, 04:54
Willi Wipp Abfrageergebniss in einer Tabelle speichern
Keine neuen Beiträge Access Tabellen & Abfragen: Unterdatenblattname: Eigenschaft lässt sich nicht speichern 0 FlyFisher 1308 05. Feb 2007, 18:49
FlyFisher Unterdatenblattname: Eigenschaft lässt sich nicht speichern
Keine neuen Beiträge Access Tabellen & Abfragen: Kann keine neuen Werte in eine Tabelle speichern 6 michasiebert 684 29. Dez 2006, 13:44
michasiebert Kann keine neuen Werte in eine Tabelle speichern
Keine neuen Beiträge Access Tabellen & Abfragen: Grafik auf SQL-Server speichern! Wie? 0 bommel007 710 20. Jul 2006, 13:37
bommel007 Grafik auf SQL-Server speichern! Wie?
Keine neuen Beiträge Access Tabellen & Abfragen: Formel in Datensatz speichern und diese berechnen 5 chrispx 3115 18. Jul 2006, 20:15
Nouba Formel in Datensatz speichern und diese berechnen
Keine neuen Beiträge Access Tabellen & Abfragen: Datennsatz nicht speichern 0 Machnik 596 31. Mai 2006, 12:41
Machnik Datennsatz nicht speichern
Keine neuen Beiträge Access Tabellen & Abfragen: Wert in Tabelle speichern 2 Gast 598 07. Mai 2006, 19:44
grimsel Wert in Tabelle speichern
Keine neuen Beiträge Access Tabellen & Abfragen: Daten von Tabelle A ind Tabelle B speichern 1 Gast 679 21. Feb 2006, 20:56
magnum Daten von Tabelle A ind Tabelle B speichern
 

----> Diese Seite Freunden empfehlen <------ Impressum - Besuchen Sie auch: Microsoft Excel-Formeln