Datenbank-Programmierung


Theorie & Praxis

Aus welchen Gründen landete ich bei der Datenbank-Entwicklung?
Nun, es gibt viele Bereiche, in denen fleißig Daten gesammelt werden müssen. Jeder braucht irgendwo Anschriften, E-Mail-Adressen, Tabellen usw.

Viele Hobbys bringen es mit sich, Daten über etwas zu speichern und zu sortieren. Also braucht man eigentlich nicht lange zu suchen, um Einsatzmöglichkeiten für Datenbanken zu finden.
Geeignete Beispiele sind u.a. Bücher, CDs, Videocassetten, Briefmarken.
Und wenn man sich lange genug mit eher simplen Einstiegsmodellen beschäftigt hat und Spaß daran findet, werden die Projekte dann auch größer und professioneller.

Datenbank-Design

Bevor es an die eigentliche Programmierung einer Datenbank-Anwendung mit Delphi geht, steht davor noch das Design der benötigten Tabellen.
Dieser Teil ist zugegeben eher theoretischer Natur (und daher für Programmierer wohl eher langweilig), aber dafür umso wichtiger.
Zumindest eine Art Grundstruktur der erforderlichen Tabellen sollte vor Beginn der Entwicklung aufgestellt werden.

Ich habe irgendwann angefangen, mir die Tabellen mit Feldnamen und Feldtypen einfach auf ein Blatt Papier zu zeichnen. Die Beziehungen zwischen den Tabellen habe ich dann mit Pfeilen dargestellt. So kann ich immer wieder auf das Datenmodell zurückgreifen und evtl. Änderungen schnell und übersichtlich erledigen.

Diese Tabellenzeichnungen gehören bei mir inzwischen zu den ständigen Projektunterlagen.
Damit fällt es mir erheblich leichter, auch während der eigentlichen Programmierung den Überblick zu behalten und z. B. schnell mal nach mir entfallenen Feldnamen zu suchen.

Verwndung von Daten-Modulen

Da ich meine ersten Datenbank-Programme nach einem Lehrbuch über Delphi 1 entwickelt habe, stehe ich der Verwendung von Daten-Modulen eher skeptisch gegenüber.
Die direkte Zuweisung von TTable- und TQuery-Komponenten in das entsprechende Formular erleichtern meines Erachtens die Programmierarbeit mehr als die getrennte Kapselung in einem separaten Modul.

Sourcecode-Beispiele aus dem RAS-Timer

Bei der Entwicklung des Online-Counters RAS-Timer hatte ich einige mit Datenbanken verbundene Probleme zu lösen. Einige Ideen möchte ich hier vorstellen.

Mit Rechtsklick aktuellen Provider in der Statuszeile ändern
Um im Hauptmenü des RAS-Timers den Provider schneller als über ein extra zu öffnendes Fenster zu ändern, ließ ich per Rechtsklick ein dynamisches PopUpMenü erstellen. Hier ein Auszug aus dem Quelltext:

procedure TForm1.MakePopUp;
var
i: integer; NewItem: TMenuItem; PopUp: TPopupMenu;
begin
PopUp := TPopupMenu.Create(Self);
Table1.First;
for i := 0 to Table1.RecordCount - 1 do
begin

NewItem := TMenuItem.Create(Self);
NewItem.Caption := Table1.FieldByName('Name').AsString;
NewItem.OnClick := ProviderLadenClick;
PopUp.Items.Add(NewItem);
Table1.Next;
end;
Panel1.PopupMenu := PopUp;
end;

{Laden des angeklickten Menüpunktes}
procedure TForm1.ProviderLadenClick(Sender: Tobject);
var
ProviderName: String;
begin
ProviderName := (Sender as TMenuItem).Caption;
Table1.FindKey([ProviderName]);
Label1.Caption := ProviderName;
end;

Erklärung:
Mit einer for-to-Schleife lasse ich alle in Table1 gespeicherten Provider als Menü-Items für das neue PopUp-Menü erstellen.
Durch das Anklicken des entsprechenden Menü-Items wird der passende Provider ausgewählt und in Label1.Caption in der Statuszeile angezeigt.

Während der Zeitmessung Sicherungsdatei führen
Diese Problemlösung fiel mir ein, als mein PC bei laufender Zeitmessung abstürzte und die laufende Aufzeichnung mit ins Nirvana riß.
Also ersann ich für die wichtigen Daten einen passenden Record-Datentyp und lasse wärend der Zeitmessung mit Hilfe eines Timers diese Daten regelmäßig in einer Datei sichern.

Bei einem normalen Programmende wird diese Datei dann gelöscht, während sie bei einem Systemabsturz in der letzten Aktualisierung erhalten bleibt.
Beim erneuten Programmstart forscht der RAS-Timer dann nach dieser Datei, deren Daten bei Vorhandensein in die entsprechende Tabelle geschrieben werden.
Hier die entscheidenden Quelltexte:

TSicherung = record
Provider: Integer;
Weekday: array[0..11] of char;
Datum, Anfang, Ende, Zeit: TDateTime;
Preis, Minuten: real;
end;

...

Datei: File of TSicherung;

...

procedure TForm3.DatenSichern; {Jede Minute Verbindungsdaten sichern}
var Sicherungsdatei: TSicherung;
begin
day := DayOfWeek(Date);
Weekday := Wochentag(day);
Sicherungsdatei.Provider := Table1.FieldByName('Nummer').AsInteger;
Sicherungsdatei.Datum := Date;
StrPCopy(Sicherungsdatei.Weekday, weekday);
Sicherungsdatei.Anfang := Start;
Sicherungsdatei.Ende := Time;
wert := Berechnen(Sicherungsdatei.Ende, start);
Sicherungsdatei.Zeit := zeit;
Sicherungsdatei.Preis := wert;
Sicherungsdatei.Minuten := sekunden;
AssignFile(Datei, 'Sicherung.dat');
Rewrite(Datei);
Write(Datei, Sicherungsdatei);
CloseFile(Datei);
end;

procedure TForm3.ReadDatei; {Nach Systemabsturz (= Sicherung.dat nicht gelöscht) letzte Verbindungsdaten in Datenbank schreiben}
var Lesedatei: file of TSicherung; Lesen: TSicherung;
begin
AssignFile(Lesedatei, 'Sicherung.dat');
Reset(Lesedatei);
Read(Lesedatei, Lesen);
CloseFile(Lesedatei);
with Table2 do
begin

Append;
FieldByName('Provider').Asinteger := Lesen.Provider;
FieldByname('Datum').AsDateTime := Lesen.Datum;
FieldByName('Wochentag').AsString := Lesen.Weekday;
FieldByName('Anfang').AsDateTime := Lesen.Anfang;
FieldByName('Ende').AsDateTime := Lesen.Ende;
FieldByName('Zeit').AsDateTime := Lesen.Zeit;
FieldByName('Preis').AsString := FloatToStrF(Lesen.Preis, fffixed, 6, 2);
FieldByName('Minuten').AsFloat := Lesen.Minuten;
Post;
end;
DeleteFile('Sicherung.dat');
end;

SQL-Abfragen mit TQuery
Zur Anzeige der Verbindungen des jeweiligen Providers ließ ich eine SQL-Abfrage von einer TQuery dynamisch erstellen. Bemerkenswert ist dabei der geringe Unterschied zwischen dem Aufruf für einen einzelnen Provider und dem für alle Provider.
Beachten Sie in den folgenden Quelltexten die hervorgehobenen Unterschiede:

Für einen Provider:
with Query1 do
begin
Close;
SQL.Clear;
SQL.Add('SELECT v.Datum, v.Wochentag, v.Anfang, v.Ende, v.Zeit,');
SQL.Add('v.Preis, p.Name');
SQL.Add('FROM verbind v, provider p');
SQL.Add('WHERE(v.provider = :nummer)');
SQL.Add('AND (v.datum BETWEEN :Anfang AND :Ende)');

ParamByName('Anfang').AsDate := Anfang;
ParamByName('Ende').AsDate := Ende;
Open;
end;

Für alle Provider:
with Query1 do
begin
Close;
SQL.Clear;
SQL.Add('SELECT v.Datum, v.Wochentag, v.Anfang, v.Ende, v.Zeit,');
SQL.Add('v.Preis, p.Name');
SQL.Add('FROM verbind v, provider p');
SQL.Add('WHERE v.datum BETWEEN :Anfang AND :Ende');
ParamByName('Anfang').AsDate := Anfang;
ParamByName('Ende').AsDate := Ende;
Open;
end;

Weitere Quelltext-Beispiele werden demnächst an dieser Stelle folgen.

© by Jörg Lipinski