Allgemeines zu Indizes
Ein Index ist eine Art Stichwortverzeichnis einer Tabelle, das in einer speziellen Ordnung angelegt wird und dadurch einen sehr schnelle Suche nach Datensätzen erlaubt. Zur Bildung eines solchen Stichwortverzeichnisses können im Prinzip alle Feldtypen verwendet werden, ausgenommen sind hier nur Memos, Blobs und Relationen. Indizes werden benötigt
- zur schnelle Suche nach bestimmten (indizierten Feldern)
- zum (relationalen) Verknüpfen von Tabellen
- zum geordneten (sortierten) Zugriff auf die Zeilen eine Tabelle
Wird beispielsweise in einer Adresstabelle ein Index über das Feld "Name" erzeugt, so wird eine neue Tabelle mit zwei Feldern angelegt: Das erste enthält den Inhalt des jeweiligen Namenfeldes, das zweite die physikalische Satznummer des zugehörigen Datensatzes. Diese Tabelle wird in einem ganz speziellen Format angelegt, es handelt sich um einen sog. B-Tree (eine hocheffiziente Ordnungs- und Suchstruktur). Und weil die Indizes in externen Dateien abgelegt werden, spricht man bei den Indizes der tdbengine auch von externen Indizes. Eine Reihe von Indizes wird automatisch angelegt:
Falls die Tabelle ein AUTO-INCREMENT-Feld enthält, wird über dieses Feld eine Index erzeugt (Extension .inr).
Falls die Tabelle ein AUTO-INCREMENT-Feld enthält, wird gemäß der Struktur ein ID-Index erzeugt (Extension .id)
Jedes Link-Feld, das ohne U-Spezifikation in einer Tabelle enthalten ist, führt zu einem Index über dieses Feld (Extension .in1 bis .in9).
Zusätzliche Indizes können ebenfalls in der Strukturdefinition angegeben werden:
ind-name = Name der Indexdatei mit Extension .ind
ind-def = Indexbeschreibung (siehe unten)
Wichtig: Die Namen der Indizes sollten derzeit (ohne Extension) nicht mehr als 8 Zeichen umfassen und nur Buchstaben (ohne Umlaute) enthalten.
Die Indizes des tdbengine
Über jede Tabelle können beliebig viele (in der aktuellen Version 6.2.6 bis zu 15) Indizes erzeugt werden. Die tdbengine bietet hierarchische Indizes mit bis zu 10 Hierarchiestufen und berechnete Indizes. Die Definition eines Index erfolgt über eine Indexbeschreibung. Diese hat folgendes Format:
Hierarchischer Index::=Feld-Def{","Feld-Def}.
Feld-Def::=["$"]Feld[":"Zahl]
Feld::=Feldbezeichner|Feldnummer
Berechneter Index::="("Ausdruck")"[":"Zahl]
Beispiele:
Hierarchischer Index: Name,Vorname:1,PLZ
Berechneter Index: (Name+'/'+Vorname):40
Die optionale Angabe :
Zahl nach einem Feld (bzw. nach einem Ausdruck) wird nur bei String-Feldern (bzw. bei String-Ausdrücken) ausgewertet und gibt an, wieviele Zeichen des Strings (maximal) in die Indexinformation übernommen werden.
Folgende Einschränkungen für Indexbeschreibungen gelten derzeit (Version 6.2.9):
- Der Ausdruck eines berechneten Index darf derzeit 40 Zeichen nicht überschreiten.
- Die Indexinformation (Summe aller Hierarchiestufen) darf maximal 256 Bytes umfassen.
Funktionen zum Erzeugen, Löschen und Auffrischen von Indizes
Hinweis: Die folgenden Funktionen werden nur dann ausgeführt, wenn die Tabelle mit dem Recht "Indizieren" (=8) geöffnet wurde. Index erzeugen
GenIndex(db : REAL; IndDef : STRING; IndName : STRING) : REAL db : Tabellenhandle von OpenDB()
IndDef : Indexbeschreibung als String
IndName : Dateiname des Index (ohne Pfad, mit .ind' als Extension)
Rückgabewert:
0 : Funktion konnte nicht ausgeführt werden (Fehlercode mit TDB_LastError)
1..15 : Nummer des neu erzeugten Index
Häufigster Fehler: "Index existiert bereits". Die tdbengine erzeugt keinen Index, wenn eine (Index-)Datei gleichen Namens im Verzeichnis der zu indizierenden Tabelle ist.
Wichtig: Der Index wird immer im gleichen Verzeichnis wie die zugehörige Tabelle abgelegt. Der Name der Indexdatei sollte immer die Extension .ind haben. Die Dateinamen sollten der 8.3-Konvention entsprechen, also xxxxxxxx.ind. Auch Verzeichnisangaben sind nicht zulässig!
In den folgenden Funktionen kann der Parameter Index entweder durch den Indexnamen (mit Extension) oder durch die Indexnummer angegeben werden (gilt erst ab Version 6.2.6, frühere Versionen nur Indexummer bzw. nur Indexname bei FindRec() ).
Index löschen
DelIndex(db : REAL; Index) : REAL db : Tabellenhandle von OpenDB()
Index: Indexname (ohne Pfad, aber mit Extension) oder Indexnummer
Rückgabewert: 0 = Funktion erfolgreich ausgeführt, sonst Fehlercode
Häufigster Fehler: "Index ist noch in Gebrauch". Solange der Index der aktive Zugriff einer geöffneten Tabelle ist, kann er nicht entfernt werden.
Achtung: Der betreffende Index wird definitiv gelöscht und aus der Tabelle entfernt. Die Funktion sollte mit größter Vorsicht eingesetzt werden: Da Tabellen zu mehreren Projekten gehören können, weiss ein Projekt oft nichts von der Notwendigkeit eines Index in einem anderen Projekt. Nach DelIndex werden alle höheren Indexnummer um 1 dekrementiert.
Index auffrischen
RegenInd(db : REAL; Index) : REAL db : Tabellenhandle von OpenDB()
Index: Indexname (ohne Pfad, aber mit Extension) oder Indexnummer
Rückgabewert: 0 = Funktion erfolgreich ausgeführt, sonst Fehlercode
Alle Indizes werden automatisch mit der zugehörigen Tabelle aktualisiert, so dass sich im Normalfall keine Notwendigkeit zum Auffrischen ergibt. Allerdings wird im Laufe der Zeit (nach vielen Änderungen an der Tabelle) die Auslastung des zugrunde liegenden B-Trees immer schlechter, so dass dann das Auffrischen eines Index dazu führt, dass
- die Suchzeit etwas verbessert wird (allerdings kaum messbar)
- der Platzbedarf auf der Festplatte deutlich reduziert wird.
Alle Indizes auffrischen
Refresh(db : REAL) : REAL
db : Tabellenhandle von OpenDB()
Rückgabewert: 0 = Funktion erfolgreich ausgeführt, sonst Fehlercode
Damit werden sämtliche Indizes einer Tabelle aufgefrischt.
Achtung: Die Funktionen zur Index-Aufrischung dürfen nicht auf Tabellen angewandt werden, die über ScanRecs() im Minimalmodus erzeugt wurden.
Indexbeschreibung ermitteln
IndexDef(db : REAL; Index) : STRING
Zugriffsreihenfolge ermitteln und festlegen
Für jede geöffnete Tabelle kann eine Zugriffsreihenfolge eingestellt werden, die für folgende Funktionen relevant ist: Die Zugriffsreihenfolge ist auch in einer SUB ... ENDSUB Anweisung relevant, wenn hier keine Sortierreihenfolge angegeben wird.
Access(db : REAL[; Index]) : REAL
Ohne Index-Paramter wird die aktuelle Zugriffsreihenfolge zurückgeliefert (erst ab Version 6.2.6):
-2 : Markierungsliste
-1 : Physikalische Ordnung
0 : AUTO-INCREMENT (falls vorhanden)
1: ID-Index (falls vorhanden)
1(2)..15 : entsprechend Indexnummer
Beim Index kann zusätzlich zu den Indexnamen (oder -nummern) noch folgendes angegeben werden:
-2 oder "Markierung"
-1 oder "Nummer"
IndexNo(db : REAL) : REAL
Liefert Zugriffsreihenfolge (alte Version):
-2 : Markierungsliste
-1 : Physikalische Ordnung
0 : AUTO-INCREMENT (falls vorhanden)
1: ID-Index (falls vorhanden)
1(2)..15 : entsprechend Indexnummer
IndName(db : REAL; Index) : STRING
Liefert den Namen des angegeben Index.
Datensatz über Index suchen
FindRec(db : REAL; SearchStr : STRING [;Index[;Mode : REAL]]) : REAL db : Tabellenhandle von OpenDB()
SearchStr : gesuchte Information
index : Indexname oder Indexnummer(>=0)
Mode : 0 (Vorgabe) -> Suche nach Eintrag >= SearchStr; 1 -> Suche nach Eintrag = SearchStr
Rückgabewert: 0 -> kein Datensatz gefunden, ansonsten RecNo(db)
Bemerkungen: Der SearchStr muss adäquat zur Index-Beschreibung aufgebaut sein. Enthält eine Komponente ein Komma, so muss diese geklammert werden.
Beispiele:
Index-Beschreibung ="Name,Vorname,Ort"
SearchStr = "Schwalm,Till,München", "Lichtenberg,Franz,(München,Moosach)"
Werden die Informationen vom Anwender geholt, ist eine Klammerung für alle Komponenten sinnvoll:
SearchStr:='('+s_Name+'),('+s_Vorname+'),('+s_Ort+')'
Wird kein Index angegeben, so wird im aktuellen (mittels Access() eingestellten) Index gesucht. Steht der Zugriff nicht auf einem Index (sondern auf Nummer oder Markierung), so erfolgt die Fehlermeldung "Illegaler Zugriff").
Mit dem Modus 0 wird der (bzgl. der Index-Ordnung) kleinste Eintrag gesucht, der gleich oder größer dem Gesuchten ist. Ist hier kein Eintrag vorhanden, so wird der größte Eintrag gesucht, der gleich oder kleiner dem Gesuchten ist. In diesem Modus wird also nur dann 0 zurückgeliefert, wenn die Tabelle komplett leer ist.
Im Modus 1 wird hingegen nur dann ein Wert ungleich 0 geliefert, wenn ein Eintrag gefunden wird, der in allen Komponenten mit der Suche übereinstimmt.
Beispiel:
Tabelle db:
| RecNo(db) |
Name |
Vorname |
Ort |
| 1 |
Meier |
Franz |
München |
| 2 |
Asbacher |
Edeltraud |
Frankfurt |
| 3 |
Grünwalder |
Antonia |
Erfurt |
| 4 |
Meier |
Hans |
Hamburg |
| 5 |
Vogelsang |
Ottfriede |
Gelsenkirchen |
Index 1 Beschreibung: "Name,Vorname,Ort"
Index 2 Beschreibung: "(Name+'/'+Vorname)"
Index 1 (intern)
| Index Information |
RecNo(db) |
| Asbacher,Edeltraud,Frankfurt |
2 |
| Grünwalder,Antonia,Erfurt |
3 |
| Meier,Franz,München |
1 |
| Meier,Hans,Hamburg |
4 |
| Vogelsang,Ottfriede,Gelsenkirchen |
5 |
FindRec(db,"Meier,Franz,München",1,0) -> 1
FindRec(db,"Meier,Franz,München",1,1) -> 1
FindRec(db,"Meier,Franz",1,0) -> 1
FindRec(db,"Meier,Franz",1,1) -> 0
Index 2 (intern)
| Index Information |
RecNo(db) |
| Asbacher/Edeltraud |
2 |
| Grünwalder/Antonia |
3 |
| Meier/Franz |
1 |
| Meier/Hans |
4 |
| Vogelsang/Ottfriede |
5 |
FindRec(db,"Meier/Hans",2,0) -> 4
FindRec(db,"Meier/Hans",2,1) -> 4
FindRec(db,"Meier",2,0) -> 1
FindRec(db,"Meier",2,1) -> 0
Filter setzen
Durch einen Index wird eine Tabelle in eine Ordnung gebracht. Mit einem Filter wird bezüglich dieser Ordnung eine zusammenhängende Teilmenge (ein Ausschnitt) aus der Tabelle definiert.
SetFilter(db : REAL; VON [, bis] : STRING) : REAL
db : Tabellenhandle von OpenDB()
von, bis : Indexinformation gemäß Indexbeschreibung
Rückgabewert: Immer 0
SetFilter() ist nur wirksam, wenn der Zugriff auf einen Index gesetzt wurde, also erst nach Access(db,...).
Ein Filter ist solange aktiv, bis er
- mit SetFilter(db,'') aufgehoben wird, oder
- die Primärtabelle gewechselt wird
Ist ein Filter aktiv, führt FirstRec ein FindRec(db,von) aus, liefert aber nur einen Wert, wenn der gefundene Datensatz gleich oder größer dem Inhalt von "von" ist. Entsprechendes gilt für LastRec. NextRec bzw PrevRec liefern nur dann Ergebnisse, wenn die gefundenen Werte im aufgespannten Bereich liegen. Auch SUB liefert nur Datensätze aus diesem Bereich. Der Parameter bis kann nur weggelassen werden, wenn die zugehörige Indexbeschreibung ausschließlich String-Felder enthält:
SetFilter(db,von) steht dann als Abkürzung für SetFilter(db,von,von+chr(255)).
Bei einem hierarchischen Index werden nicht angegebene Komponenten bei von mit dem kleinsten, bei bis mit dem größten möglichen Wert aufgefüllt.
Die Filter-Automatik der tdbengine
Bei der Bearbeitung von Selektionen versucht die tdbengine die zu lesende Datenmenge und damit den Selektionsaufwand zu minimieren, indem sie alle vorhandenen Indizes daraufhin untersucht, ob ein passender Filter gesetzt werden kann. Diese Automatik arbeitet unabhängig von der Reihenfolge der logischen Operationen und auch unabhängig von der verkürzten Logikauswertung. Die Automatik wird abgeschaltet, wenn zum Zeitpunkt der Selektion
- der Zugriff über die Markierungsliste erfolgt, oder
- der Zugriff auf einen anderen Index als den .inr (über die Autonummern) gesetzt ist.
Beispiel für die Arbeitsweise der Filter-Automatik:
[STRUCTURE]
field_1=Name,STRING,40
field_2=Vorname,STRING,30
field_3=Strasse,STRING,40
field_4=PLZ,STRING,5
field_5=Ort,STRING,30
[INDEX]
index_1=adr_name.ind:Name,Vorname
index_2=adr_plz.ind:PLZ,Ort
| Selektion |
Index |
Filter von ... bis |
| $Name like "Meier" |
adr_name.ind |
meier |
| $Name like "Meier*" and $Vorname like "Hans" |
adr_name.ind |
meier,hans |
| $PLZ from "70000" to "79999" |
adr_plz.ind |
70000 ... 79999,chr(255)+chr(255)... |
| $Name like "*gruber" and $PLZ like "8*" |
adr_plz.ind |
8
( "*gruber" erlaubt keinen Filter) |
| $Name like "hinter*meier*" and $PLZ like "8*" |
adr_name.ind |
hinter
(4 Zeichen hei "hinter" gegenüber 1 Zeichen bei "8") |
Die Filter-Automatik sorgt dafür, dass die Funktion SetFilter nur in wenigen Spezialfällen eingesetzt werden muss.
|