Deutsch English
Blog
Home
Über tdbengine
Newsletter
Download
Helpware
Chat
Dokumentation
Installation
Konfiguration
Erste Schritte
Variablentypen
Laufzeitschalter
Textdateien
Systemfunktionen
Tabellenfunktionen
Indexfunktionen
Volltext-Indizierung
Memos und Blobs
Semaphoren-Konzept
Fehlermeldungen
Tipps für PHP Programmierer
Locking daemon
Einführungskurs
Befehlsreferenz
HOWTO - Wie kann ich...?
Projekte
Links
Benchmarks
Bug Reporting
Supportanfrage
 
Home    Überblick    Suche    Impressum    Kontakt    Mitglieder
Datenbank-Funktionen

Erzeugen einer Tabelle


MakeDB(Tabellenname, Paßwort, Verschlüsselungscode, Strukturdefinition [,Quelle]) : Fehlercode

Die Strukturdefinition ist der Name einer Textdatei, die folgendermaßen aufgebaut ist:

[STRUCTURE]
field_1=fieldspec
field_2=fieldspec
...
[INDEX]
id=indexdef
index_1=ind-name:ind-desc
index_2=ind-name:ind-desc
...

Der Abschnitt unter [INDEX] ist nur erforderlich, wenn die Tabelle automatisch numeriert wird.

Eine Feldspezifikation fieldspec hat folgende Struktur:
Feldbezeichner,Typdefinition[,(Importfeld)]

Als Feldbezeichner sind alle Bezeichner zugelassen, die mit einem Buchstaben oder Unterstrich beginnen und nur aus Buchstaben, Ziffern und Unterstrich bestehen. Die maximale Länge beträgt 35 Zeichen.

Folgende Typdefinitionen sind zugelassen:

Zeichenketten

STRING,Länge (Länge von 1 bis 255) Stringfeld mit der angegeben Maximallänge


Zahlen

NUMBER,NumCode[,Nachkommastellen][,U] numerisches Feld je nach NumCode

NumCode Typ Wertbereich
1 Byte Ganze Zahlen von 0 bis 255
2 16-Bit-Integer Ganze Zahlen von - 32768 bis + 32767
4* 32-Bit Integer Ganze Zahlen von - 2147483648 bis + 2147483647
6** 6-Byte REAL (altes Borland-Format) Fließkommazahlen von -2.9 x 10^39 bis +1.7 x 10^38 bei 11-12 sign. Stellen
8*** 8-Byte-REAL Fließkommazahlen (ANSI double) Fließkommazahlen von -5.0 x 10^324 bis +1.7 x 10^308 bei 15-16 sign. Stellen

*) nur tdbengine, nicht kompatibel zur VDP oder TurboDatenbank
**) kompatibel zu allen VDP- und TurboDatenbank-Versionen
***) nur tdbengine und VDP (ab 2.5)

Nachkommstellen werden nur bei den REAL-Typen berücksichtig. Sie sind wesentlich für den Datentransfer mit den Funktionen GetField und SetField sowie beim Export in andere Datenbankformate. Intern werden alle REAL-Typen immer mir der optimalen Genauigkeit gespeichert.

Wird der Parameter U angegeben, so wird in Selektion zwischen dem Wert 0 und "undefiniert" unterschieden.

Memos und Blobs

MEMO ergibt ein Memofeld

Der Inhalt eines Memofeldes ist ein Verweis auf eine zusätzliche Memodatei. In Memodateien werden auschließlich unformatierte Texte gespeichert.
BLOB [,blocksize] ergibt ein Blobfeld

Der Inhalt eines Blobfeldes ist ein Verweis auf eine zusätzliche Blobdatei In Blobdateien werden beliebige Daten gespeichert. Mit blocksize kann die Größe der Cluster in der Blobdatei festgelegt werden (Minimum = 64, Maximum = 8192, Default = 4096). Pro Eintrag in die Blobdatei wird im Schnitt ein halber Cluster verschwendet, aber zu kleine Cluster verlängern die Zugriffszeiten.


AUTO-INCREMENT
AUTO[,Startnummer] Autonummern-Feld (AUTO INCREMENT)

Die Zählung beginnt (in einer neuen Tabelle) mit der Startnummer (bzw 1, falls keine angegeben wurde)


Datum und Zeit

DATE Datumsfeld (dd.mm.yyyy)
TIME Zeitfeld (hh:mm)
UTIME UNIX-Timestamp (Sekunden seit 1.1.1970)


Aufzählung

SELECT,Wert1,Wert2,Wert3,... Auswahlfeld mit dem angegebenen Wertbereich


Verknüpfungen

REL Relationsfeld zwischen den angegebenen Tabellen
Achtung: Die tdbengine erzeugt nicht automatsich die zugehörige Relationstabelle. Hierfür steht die Funktion GenRel zur Verfügung.
LINK,Tabelle ADL-Linkfeld zur angegebenen Tabelle
Es wird nicht gepüft, ob die Tabelle existiert oder ob die automatisch numeriert wird.

Importfelder festlegen



Ist in MakeDB() eine Quelle angegeben, so wird das spezifizierte Feld durch den Inhalt des angegebenen Importfeldes der Quelle ersetzt. Ist kein Importfeld angegeben, so wird der spezifizierte Feldname verwendet.

Wird als Quelle der gleiche Dateiname wie unter Ziel angegeben, so wird die Tabelle entsprechend restrukturiert. Als Abkürzung für den gleichen Dateinamen ist das Zeichen "@" erlaubt.

Hinweis: Die Strukturdefinition ist eigentlich eine Maschinendatei, die von einem Hilfsprogramm durch Interaktion mit dem Anwender erzeugt wird. Wenn Sie eine Strukturdefinition von Hand erstellen, sollten Sie unbedingt folgendes beachten:

  • Die Numerierung der Felder muß lückenlos sein.
  • Keine Feldnummer darf doppelt vorkommen.
  • Fügen Sie neue Felder immer am Ende an, da die Reihenfolge nicht wesentlich ist.
  • Wenn Sie im Zuge einer Umstrukturierung einen Feldbezeichner ändern wollen, so vergessen Sie nicht, den bisherigen Bezeichner als Importfeld anzugeben, da andernfalls der Feldinhalt verloren ist.

Die Umstrukturierung einer Tabelle ist immer ein kritischer Vorgang, wenn die Tabelle bereits Informationen enthält. Sie können zur Datensicherheit beitragen, indem Sie auf die direkte Umstrukturierung verzichten, und statt dessen die bisherige Tabelle mit RenTable() umbenennen und die umbenannte Tabelle als Quelle angeben. Falls irgendetwas schiefgeht, können Sie immer noch auf die bisherige Tabelle zugreifen.

)

Tabellenstruktur ermitteln

GetDef(db : REAL; structfile : STRING) : REAL

db : Tabellenhandle von OpenDB()
structdef : Pfad zu einer Textdatei, in die die Strukturdefinition geschrieben wird.

Rückgabewert: 0 = ok, sonst Fehlercode

Die Tabellenstruktur der geöffneten Tabelle db wird in die angegebene Datei geschrieben. Diese kann dann wiederum zum Erzeugen neuer Tabellen verwendet werden. Als Ziel sind auch Ramtexte zulässig.

Beispiel: Leere Kopie einer geöffneten Tabelle db erzeugen

GetDef(db,'ramtext:text:structure')
MakeDB('kopie_von'+DBName(db),'',0,'ramtext:text:structure')

Löschen einer Tabelle

DelDB(Tabellenname, Paßwort : STRING; Code : REAL) : Fehlercode

Löscht die Tabelle inklusiver aller Zusatzdateien wie Memo, Blob und Indizes.


Umbenennen einer Tabelle

RenDB(Datename,Paßwort,Code,NeuerName) : Fehlercode

Benennt die Tabelle mit dem angegeben Datenamen und den neuen Namen um. Es werden ebenfalls Memo, Blob und Indexdateien bei Bedarf umbenannt.


Tabellen öffnen und schließen

OpenDB(Dateiname[,Paßword[,Code[,Modus]]]) : Tabellennummer

Modus ist ein Addition der einzelner Grundmodi, mit denen die gewünschte Zugriffsart gekennzeichnet wird:

0 : (Vorgabe) Es dürfen keine Veränderungen an der Tabelle vorgenommen werden.
1 : Es dürfen neue Datensätze erzeugt werden
2 : Es dürfen bestehende Datensätze überschrieben werden
4 : Es dürfen Datensätze gelöscht werden
8 : Es dürfen Indizes erzeugt und gelöscht werden

Der Modus 15 erlaubt somit sämtliche Modifikationen an der Tabelle.

Hinweis: Tritt beim Öffnen der Tabelle ein Fehler auf, so wird das Programm normalerweise mit einer entsprechenden Fehlermeldung beendet. Wenn die interne Fehlerbehandlung abgeschaltet wurde, ist der Rückgabewert der Funktion 0, und der Fehler kann mit TDB_ErrorCode ermittelt werden.

Beispiel:

VAR d : INTEGER
.EC 1
IF d:=OpenDB("database/adressen.dat")=0 THEN
  CGIWriteHTML("Beim Öffnen der Tabelle ist folgender Fehler aufgetreten: ")
  CGIWriteHTML(TDB_ErrorStr(TDB_LastError))
ELSE
  CGIWriteHTML(Str(FileSize(d))+" Datensätze stehen zu Ihrer Verfügung")
END

Anders als die TurboDatenbank öffnet die tdbengine über L- oder R-Felder verknüpfte Tabellen nicht automatisch, sondern diese müssen bei Bedarf separat mit OpenDB() geöffnet werden. Dabei ist zu beachten, daß immer die zuletzt geöffnete Tabelle in die bisherige Relationsstruktur eingebaut wird. Das bedeutet beispielsweise, daß Relationstabellen immer erst nach den Tabellen, zwischen denen sie die Verknüpfung definiert, geöffnet werden darf. Wird eine Tabelle mit einem L-Feld geöffnet, muß die zugehörige Link-Tabelle schon geöffnet sein, damit die Verknüpfung aktiv wird.

CloseDB(Tabellennummer)

Eine geöffnete Tabelle kann jederzeit mit CloseDB() wieder geschlossen werden.

Am Programmende wird automatisch CloseDB() für aller geöffneten Tabellen ausgeführt.

Hinweis: Im Zuge eines guten Programmierstils sollten Sie alle Tabellen, die von Ihrem Programm geöffnet werden, auch wieder schließen.


Tabellengröße ermitteln

FileSize(Tabellennummer[,Modus] : REAL) : REAL

Liefert die Anzahl der Datensätze in der Tabelle.

Modus = 0 (Vorgabe) Standard -> Anzahl der Datensätze in der zugehörigen .dat (bzw. -1 bei Volltext-Stichwort-Listen).
Modus =1 Erweitert ->
  Anzahl der ID-Einträge im ID-Index bei Stichwortlisten, die mittels ScanRecs() und Modus=4 erzeugt wurden
  Anzahl der Einträge im IN2-Index bei REL-Dateien, die mittels ScanRecs() im Modus=4 erzeugt wurden.


Datensätze lesen, schreiben und löschen

Die tdbengine stellt für jede Tabelle einen Datensatzpuffer zur Verfügung. Alle Datenfeld-Funktionen beziehen sich auf diesen Datensatzpuffer.

ReadRec(Tabellennummer,Satznummer) : REAL

Liest den Datensatz mit der Satznummer in den Datensatzpuffer der Tabelle mit der angegebenen Tabellennummer. Falls Satznummer=0, wird ein leerer Datensatz bereitgestellt. Das Funktionergebnis ist die Satznummer, wenn der Datensatz gelesen werden konnte, andernfalls 0.

Bei einer illegalen Satznummer wird ein Laufzeitfehler ausgelöst.

WriteRec(Tabellenummer,Satznummer) : REAL

Schreibt den aktuelen Datensatzpuffer an der abgegeben Satznummer in die Tabelle. Zulässige Satznummern sind 1..FileSize(Tabellennummer)+1.

Illegale Satznummern führen zu einem Laufzeitfehler.

Liegt die Satznummer zwischen 1 und FileSize(Tabellennummer) so wird der entsprechende Datensatz überschrieben, andernfalls wird die Tabelle um einen Datensatz erweitert.

DelRec(Tabellennummer,Satznummer) : REAL

Löscht den Datensatz mit der angegebenen Satznummer. Die tdbengine geht beim Löschen eines Datensatzes so vor:

  • Der Datensatz wird aus sämtlichen Indizes der Tabelle gelöscht.
  • Handelt es sich um den letzen Datensatz der Tabelle, so wird die Tabelle um einen Eintrag verkürzt.
  • Andernfalls wird der letzte Eintrag der Tabelle an die Position des zu löschenden Datensatzes kopiert, alle Indizes entsprechend geändert und schließlich die Tabelle um einen Eintrag verkürzt.
Dieses Vorgehen hat zwar den Vorteil, daß die Tabelle immer nur aktuelle Datensätze enthält. Vorsicht ist jedoch geboten, wenn sich Funktionen auf physikalische Satznummern beziehen. Zwar liefert RecNo() immer die aktuelle Satznummer (auch wenn sich diese infolge Löschungen verschoben haben). Bei CGI-Programmen ist jedoch darauf zu achten, daß die Verbindung immer wieder abgebrochen wird, und die Informationen über CGI-Variable an den nächsten Aufruf übergeben wird. Hier kann der Bezug auf physikalische Satznummern fatale Folgen haben, denn die kann sich von einem Aufruf zum nächsten bereits geändert haben.

Abhile schafft der Bezug auf die Autonummern der Tabelle. Dazu stellt die tdbengine zwei neue Funktionen zur Verfügung, mit der die Umwandlung von Satznummern und Autonummern stark vereinfacht wird: AutoField() und FindAuto().

AutoField(Tabellennummer) : REAL

liefert die Nummer des AUTO-Feldes (bzw. 0, wenn nicht automatisch numeriert).

GetRField(Tabellenummer,AutoField(Tabellennummer)) liefert so die Autonummer eines Satzes der Tabelle.

Dafür gibt es die Funktion

AutoRecNo(db : REAL) : REAL

db : Tabellenhandle von OpenDB()

Rückgabewert:

RecNo(db)    wenn kein AUTO-INCREMENT-Feld in db
GetRField(db,AutoField(db))  sonst

FindAuto(db,x : REAL) : REAL

db : Tabellenhandle von OpenDB()
x : (Auto-)Nummer

Rückgabewert:

x   wenn kein AUTO-INCREMENT-Feld in db
FindRec(db,Str(x),0,1)  sonst

Wann immer es möglich ist (also eine Autonummer in der Tabelle enthalten ist), sollte die Information über einen Datensatz als Autonummer zwischen CGI-Programmen erfolgen.

Eine andere Problematik steckt in der Verwendung von Volltext-Indizes, die ja auch auf physikalische Satznummern zurückgreifen. Ein Volltextindex wird nicht automatisch aktualisiert, wenn ein Datensatz gelöscht wird. Das ist auch garnicht möglich, da Volltextindizes kein Bestandteil der Tabelle sind, die tdbengine demnach auch keine Information darüber haben kann.

Ein Ausweg besteht darin, einen Volltextindex nach dem Löschen in einer Tabelle neu zu erzeugen. Bei kleiner Tabellen ist diese Vorgehensweise aufgrund der hohen Geschwindigkeit der tdbengine auch durchaus praktizierbar. Der andere, aufwendigere und auch elegantere Ausweg besteht darin, den Volltextindex dynamisch mit der Tabelle mitzuführen. Mit ScanRec() und UnScanRec() stehen dazu die benötigten Funktionen zur Verfügung. Hier nur das Grundgerüst für das Löschen eines Datensatzes mit Volltext-Index-Nachführung:

PROCEDURE DelRecWithFullText(TheTableHdl, TheRecNo : REAL)
  ReadRec(TheTableHdl,TheRecNo) // auf gewünschtem Datensatz positionieren
  UnScanRec(...) // Volltextindex für diesen Satz löschen
  IF TheRecNo<=FileSize(TheTableHdl)  THEN
    ReadRec(TheTableHdl,FileSize(TheTableHdl)) // auf letzten Datensatz positionieren
    UnScanRec(...) // Volltextindex für den letzten Satz löschen
    DelRec(TheTableHandle,TheRecNo) // Letzter Satz wird hierher verschoben
    ScanRec(...) // UND wird neu Volltext-indiziert
  ELSE
    DelRec(TheTableHdl,TheRecNo)
  END
ENDPROC

Datensätze suchen und markieren



FindRec

Sucht einen Datensatz über einen existierenden Index. Diese Art der Suche ist sehr schnell.

FindRec(db : INTEGER; cSuchStr : STRING; [Index]; [bMode : Integer]) : INTEGER

db : Tabellenhandle von OpenDB()

cSuchStr ist ein der Definition des Index entsprechender Suchstring. Ein Index, der z.B. so definiert ist "$Name, $Vorname", erwartet eine Suchanfrage wie diese: "Muster, Max".
Klammern sie die einzelnen Suchbegriffe, wenn in den Teilstrings auch Kommas ","  enthalten sein können: z.B. "(Muster),(Max, Jr)"

Index ist optional, und erwartet entweder die Indexnummer, oder den Indexnamen. Wenn der Parameter nicht übergeben wurde, wird der aktuell im Zugriff befindliche Index verwandt. Sollte im letzteren Fall kein Index verwendet werden, sondern der Zugriff entweder auf "Markierung" oder auf "Nummer" stehen, dann wird ein "Illegaler Zugriff"-Fehler ausgelöst.

bMode ist ebenfalls optional, allerdings nur dann zu setzen, wenn auch Index explizit angegeben wurde. Dieser Parameter bestimmt, ob die Suche nur einen absoluten Treffer akzeptiert, oder einfach den "erstbesten" Treffer zurückliefert. bMode = 1 erfordert also eine 100%ige Übereinstimmung, damit FindRec() eine Satznummer zurückliefert, bMode = 0 liefert ggfs. den nächstfolgenden Datensatz (in der Reihenfolge von Index) zurück, sucht somit also quasi nach ">= cSearchStr".

Das Ergebnis der Funktion ist 0, wenn die Suche mit bMode = 1 durchgeführt und kein Datensatz gefunden wurde, ansonsten die Satznummer des ersten Treffers.


FirstRec

FirstRec() liefert die Satznummer des ersten Datensatzes zurück. Dabei wird die aktuellen Sortierung (siehe Access()) berücksichtigt.

FirstRec(db : INTEGER) : INTEGER

LastRec

LastRec() gibt die Satznummer des letzten Datensatzes nach der aktuell eingestellten Sortierreihenfolge zurück.

FirstRec(db : INTEGER) : INTEGER

NextRec

Ermittelt die physikalische Satznummer des, relativ zum derzeit gelesenen, nächsten Datensatzes. Die zugewiesene Sortierreihenfolge ist entscheidend.

NextRec(db : INTEGER) : INTEGER

PrevRec

Funktioniert wie NextRec(), nur in die andere Richtung. Ermittelt die Satznummer des, relativ zum derzeit eingelesenen, vorhergehenden Datensatzes, unter Berücksichtigung der gewählten Sortierreihenfolge.

PrevRec(db : INTEGER) : INTEGER


Die Funktionen FirstRec() und NextRec() werden häufig innerhalb von Schleifen angewandt, um mehrere Datensätze der Reihe nach zu lesen.

PROCEDURE Main
  VAR db, nRec : INTEGER
  db := OpenDB("manyrow.dat")
  Access(db, "mr_name.ind")
  nRec := FirstRec(db)
  WHILE nRec > 0 DO
     ReadRec(db, nRec)

     ... // mache irgendwas damit, z.B. Ausgabe

     nRec := NextRec(db)
  END
ENDPROC

Der Zugriff auf Datenfelder



Grundsätzlich lässt sich der Zugriff auf die Felder einer Tabelle in zwei Arten unterteilen: den Lese- und den Schreibzugriff.

GetField und GetRField

Die beiden Funktionen GetField() und GetRField() lesen den Inhalt eines Feldes aus und geben ihn zurück.

GetField(db : INTEGER; [Feld]) : STRING
GetRField(db : INTEGER; [Feld]) : REAL

für [Feld] gilt jeweils:

  Feldname : STRING
  Feldnummer : INTEGER

Wenn nötig und möglich, konvertiert die tdbengine die Inhalte typgerecht um. Das heisst, beim Einsatz von GetField() auf ein Zahlen-, Datums- und Zeit- oder auch auf ein Link-Feld wird der (numerische) Inhalt passend in einen String konvertiert. Dies ist normalerweise auch problemlos möglich.
Umgekehrt ist darauf jedoch nur bedingt Verlass. GetRField() angewandt auf ein String-Feld führt zu einem Fehler, wenn der Inhalt des Feldes nicht konvertierbar ist, z.B. bei Texten. Diese lassen sich nun mal nicht so einfach in eine Zahl umwandeln.

Hinweis: GetField() ist die typunabhängiste Form um an Feldinhalte zu kommen. Das Ergebnis ist immer ein String.

GetField() und GetRField() sind nicht geeignet, um die Inhalte von MEMO- bzw. BLOB-Feldern auszulesen. Stattdessen liefern diese lediglich die Texte "MEMO" bzw. "BLOB" oder, wenn kein Inhalt vorhanden "leer" zurück. Zur Behandlung derartiger Felder lesen Sie bitte das Kapitel Memos und Blobs.


SetField und SetRField

Mit SetField() und SetRField() schreibt man Informationen in ein einzelnes Feld.

SetField(db : INTEGER; [Feld]; Wert : STRING) : STRING
SetRField(db : INTEGER; [Feld]; Wert : REAL) : REAL

für [Feld] gilt jeweils:

  Feldname : STRING
  Feldnummer : INTEGER

Zu beachten ist auch hier die Wandelbarkeit der übergebenen Informationen. In ein String-Feld kann beliebige Information geschrieben werden. Zahlen genauso wie Texte, die tdbengine wandelt alles in einen String um.
Andersherum ist das nicht immer ohne Weiteres möglich:

SetRField(db,"Geburtstag", "Alfred") // Geburtstag ist Feld vom Typ DATE

würde schon beim Kompilieren einen Fehler verursachen, da SetRField() als 3. Parameter einen REAL-Wert erwartet.

SetField(db,"Geburtstag", "Alfred") // Geburtstag ist Feld vom Typ DATE

führt jedoch "lediglich" zu einem Laufzeitfehler, der u.U. eine ganze Weile unendeckt bleiben kann.

Diese Beispiele zeigen, wie schnell sich ein Fehler einschleichen kann. Es ist daher sehr zu Empfehlen, bei Zahlenfeldern SetRField() statt SetField() zu verwenden, um konvertierungsprobleme schon während der Entwicklung auszumerzen.

Diese beiden Funktionen solten nicht im Zusammenhang mit MEMO- oder BLOB-Feldern verwendet werden. Über das Schreiben und Lesen derartiger Felder lesen Sie bitte das Kapitel Memos und Blobs.


SetFields

SetFields() erlaubt das Befüllen von mehreren Feldern gleichzeitig.

SetFields(db : INTEGER; replacestr : STRING) : INTEGER

replacestr ist eine, durch Komma miteinander verknüpfte, Kette von Key/Value-Paaren.

feld1=wert1, feld2=wert2, ... feldn=wertn

Nachdem die tdbengine lediglich Strings mit einer maximalen Länge von 255 Zeichen verwalten kann, ist der Gebrauch dieser Funktion stark eingeschränkt
Das Funktionsergebnis ist im Erfolgsfall 0, ansonsten die entsprechende Fehlernummer.

Beispiel:

IF SetFields(db, "Name='"+cName+"',Groesse="+Str(nSize)+",ID='"+cID+"'") <> 0 THEN
  //Fehler beim Speichern
END

Der Einsatz der Funktion SetFields() ist nicht sonderlich empfehlenswert, da jegliche Fehlerprüfung erst zur Laufzeit durchgeführt werden kann. Am Besten ist es, mehrere SetField() und SetRField() -Anweisungen nacheinander zu benutzen.

Markierungsfunktionen

FindAndMark()
GetMarks()
PutMarks()
FirstMark()
NextMark()
DelMarks()
RevMarks()






tdbengine Anwendungen im Web:

Open-Source Web CMS


Open-Source Bug-Tracking


Free wiki hosting

Open-Source Wiki-System

Kostenloses Foren-Hosting

Diät mit tdbengine 8-)

tdbengine chat
irc.tdbengine.org
#tdbengine

   Copyright © 2003-2004 tdb Software Service GmbH
   Alle rechte vorbehalten. / All rights reserved
   Letzte Änderung: 10.05.2004


ranking-charts.de

Programmers Heaven - Where programmers go!