Beispiel - Verknüpfungen
Beispiel - Verknüpfungen Löschen eines Datensatzes und aller verknüpfter Datensätze
- Siehe:
RecLink (),FileInfo (),LinkInfo (), Beispiel ohne Beschreibung
In diesem Beispiel werden zwei Funktionen vorgestellt, mit denen sowohl der Datensatz, der sich zur Zeit in den Feldpuffern einer Datei befindet, als auch alle mit diesem Datensatz verknüpften Datensätze gelöscht werden. Durch die Verwendung dieser Funktion kann die "Referenzielle Integrität" der Datenbank gewährleistet werden.
Der vollständige Prozedurcode ohne Kommentare kann hier angezeigt werden.
Die Aufgaben werden auf zwei Funktionen verteilt. Die Funktion DeleteRec() löscht die Datensätze. Diese Funktion wird rekursiv aufgerufen. Die Funktion Delete() ruft die Funktion DeleteRec() auf, nachdem eine Transaktion gestartet wurde. Die Verwaltung der Transaktion findet in der Funktion Delete() statt.
DeleteRec()
Dieser Funktion wird die Dateinummer übergeben, in dessen Feldpuffer der zu löschende Datensatz steht. Weitere Übergabeparameter werden nicht benötigt.
sub DeleteRec
(
aFileNo : int; // Dateinummer
) : int;
local
{
tLinkNo : int;
tLinkFileNo : int;
tErg : int;
tRecBuffer : handle;
}
Zunächst wird der Übergabeparameter geprüft. Die Funktion FileInfo (..., _FileExists ) liefert 1 zurück, wenn die angegebene Dateinummer existiert, sonst 0. Sollte die Datei nicht vorhanden sein, wird der Wert _ErrFileInvalid zurückgegeben und die Funktion beendet.
{
// Überprüfung der Übergabeparameter
if (FileInfo(aFileNo, _FileExists) = 0)
{
return(_ErrFileInvalid);
}
Der Datensatz wird jetzt gelöscht. Können verknüpfte Datensätze nicht gelöscht werden, muss dieser Datensatz wieder hergestellt werden. Dies erfolgt durch die Transaktion.
// Datensatz löschen
tErg # RecDelete(aFileNo, 0);
if (tErg != _rOk)
{
return(tErg);
}
In einer Schleife werden alle Verknüpfungen der Datei ermittelt. Existieren keine Verknüpfungen, wird die Zähl-Schleife nicht durchlaufen.
// Existiert in dieser Datei eine Verknüpfung?
for tLinkNo # 1;
loop Inc(tLinkNo);
while (tLinkNo <= FileInfo(aFileNo, _FileLinkCount))
{
Mit dem Befehl LinkInfo () wird die Zieldatei der Verknüpfung ermittelt. Da in dieser Datei Datensätze gelesen werden, erfolgt zunächst eine Speicherung der Feldpuffer dieser Datei. Dazu wird ein neuer Feldpuffer mit dem Befehl RecBufCreate () angelegt und der Inhalt der Feldpuffer mit dem Befehl RecBufCopy () kopiert.
// Nummer der Zieldatei ermitteln
tLinkFileNo # LinkInfo(aFileNo, tLinkNo, _LinkDestFileNumber);
// Puffer der Zieldatei retten
tRecBuffer # RecBufCreate(tLinkFileNo);
RecBufCopy(tLinkFileNo, tRecBuffer);
In der folgenden Schleife werden solange Datensätze gelesen, bis entweder kein verknüpfter Datensatz mehr existiert oder ein gesperrter Datensatz gelesen wurde. In diesem Fall bricht die Schleife mit einem entsprechenden Fehler ab, der später ausgewertet wird. Jeder verknüpfte Datensatz, der gelesen werden konnte, wird mit der Funktion DeleteRec() gelöscht. Hier findet ein rekursiver Aufruf statt, da der verknüpfte Datensatz natürlich weitere verknüpfte Datensätze besitzen kann, die ebenfalls gelöscht werden müssen. Um zwischen den Fällen "Fehler beim Löschen des Datensatzes" und "Fehler beim Lesen des Datensatzes" zu unterscheiden, werden die Rückgabewerte beim Löschen um 1000 erhöht.
// Verknüpften Datensatz lesen
tErg # RecLink(tLinkFileNo, aFileNo, tLinkNo, _RecLast);
while ((tErg = _rOk) or (tErg = _rLocked))
{
// Verknüpften Datensatz löschen
tErg # DeleteRec(tLinkFileNo);
if (tErg != _rOk)
{
tErg # 1000 + tErg;
}
else
{
tErg # RecLink(tLinkFileNo, aFileNo, tLinkNo, _RecLast);
}
}
Nach dem Durchlauf der Schleife, wird der ursprüngliche Inhalt des Feldpuffers wieder hergestellt. Der angelegte Puffer wird mit dem Befehl RecBufDestroy () wieder entfernt.
// Puffer der Zieldatei wieder herstellen
tRecBuffer->RecBufCopy(tLinkFileNo);
tRecBuffer->RecBufDestroy();
Konnte ein Datensatz nicht gelöscht werden, wird der komplette Aufruf mit einem Fehler abgebrochen. Dies hat zur Folge, dass die komplette Rekursion mit dem gleichen Fehlerwert beendet wird. Der erste Aufruf der Funktion DeleteRec() gibt also ebenfalls den entsprechenden Fehlerwert zurück.
// Funktion beenden, wenn ein Datensatz nicht gelöscht werden konnte
if (tErg > 1000)
{
return(tErg - 1000);
}
}
Sind alle verknüpften Datensätze einer Verknüpfung gelöscht wird im nächsten Schleifendurchlauf der Zähl-Schleife die nächste Verknüpfung behandelt.
Verfügt eine Datei, in der ein Datensatz gelöscht werden soll, über keine Verknüpfung, wird die Zähl-Schleife nicht durchlaufen, d. h. es wird lediglich der Befehl RecDelete () aufgerufen. Existieren Verknüpfungen, aber keine verknüpften Datensätze, wird die while-Schleife nicht durchlaufen und ebenfalls nur der entsprechende Datensatz gelöscht.
Delete()
Die Funktion DeleteRec() löscht alle verknüpften Datensätze bis entweder keine verknüpften Datensätze mehr vorhanden sind, oder beim Löschen eines Datensatzes ein Problem (zum Beispiel dieser Datensatz von einem anderen Benutzer bearbeitet wird) auftritt. Um zu verhindern, dass nur ein Teil der verknüpften Datensätze gelöscht werden, wird die Funktion in eine Transaktion eingeschlossen.
Eine Transaktion wird mit dem Befehl DtaBegin () eingeleitet. Verläuft das Löschen der verknüpften Datensätze ohne Probleme, liefert die Funktion DeleteRec() den Wert _rOk zurück. In diesem Falle wird die Transaktion mit DtaCommit () beendet und alle Änderungen seit DtaBegin () werden in die Datenbank übernommen. Liefert die Funktion DeleteRec() einen anderen Wert zurück, wird die Transaktion mit DtaRollback () abgebrochen und alle Änderungen seit DtaBegin () werden verworfen.
sub Delete
(
aFileNo : int; // Dateinummer
) : int;
local
{
tErg : int;
}
{
DtaBegin();
tErg # DeleteRec(aFileNo);
if (tErg != _rOk)
{
DtaRollback(false);
}
else
{
DtaCommit();
}
return(tErg);
}
Diese Funktionen können in eine beliebige Datenbank eingespielt werden. Durch ihre Programmierung passt sie sich jeder vorhandenen Datenstruktur an. In der Regel wird eine derartige Vielseitigkeit nicht benötigt und die Funktion DeleteRec() kann entsprechend vereinfacht werden.
Diese Funktionen löschen alle verknüpften Datensätze. Einmal generierte Aufträge müssen aber beispielsweise in der Datenbank verbleiben, auch wenn der Kunde dazu gelöscht wurde. In solchen Fällen können bestimmte Verknüpfungen in der Schleife ausgeschlossen werden. Ein anderer Ansatz wäre die Verknüpfungen nicht durch eine Schleife ermitteln zu lassen, sondern sie explizit anzugeben. Dies müsste dann für jede Datei gesondert erfolgen.
Bei der Programmierung mit Transaktionen ist darauf zu achten, dass jedem DtaBegin () ein DtaCommit () oder DtaRollback () folgt. Kommt es während einer Transaktion zu einem Laufzeitfehler, wird die Funktion nicht beendet und damit auch kein DtaCommit () oder DtaRollback () durchgeführt. Die Transaktion ist noch aktiv und wird unter Umständen erst zurückgesetzt, wenn der Benutzer die Datenbank verlässt. Alle Änderungen (auch Änderungen in Prozeduren) werden dann verworfen.