Datenbank Grundlagen

Gespeichert von Lemmy am So., 16.09.2018 - 19:47

In diesem Tutorial geht es ausschließlich um Datenbanken. Was ist das, wie funktioniert das und was kann man damit machen. Hier werde ich Grundlagen erläutern - von der Idee bis zur fertigen Datenbank. Die SQL-Grundlagen sowie weiterführende SQL-Techniken für den IB/FB-Server gibt's in meinem SQL-Tutorial. Historisches Der Begriff "Datenbank" ist nur schwer zu definieren, da umgangssprachlich alles mögliche mit diesem Begriff in Verbindung gebracht wird - vom Datenbankmanagementsystem (DBMS), den Datenbankfiles bis hin zu Paradox. Auf die genaue Bedeutung der eben genannten Begriffe werde ich später eingehen. Die ersten "Datenbanken" waren File-Systeme in denen die Datensätze nacheinander (sequentiell) abgespeichert wurden. Wollte man also den 20. Datensatz lesen, musste z.B. das Band zum Anfang der Datensätze (begin of file BOF) gespult werden und dann jeder einzelnen Datensatz eingelesen werden, bis man beim 20sten angekommen war. Oft wird auch eine "Datenbank" als großer Speicherraum für irgendwelche Daten missverstanden. Daten können auch in Textdateien auf der Festplatte gespeichert werden, was sicherlich keine Datenbank darstellt. Die Datenbank (oder besser das DBMS) ist dafür verantwortlich die Daten schnell wieder zu finden und das kann in einem Textfile mitunter schwierig werden. Definitionen Datenbankmanagementsystem (DBMS) Das DBMS ist das Programm, das für den Zugriff auf die DB-Files, in denen die einzelnen Daten gespeichert sind, zuständig ist. DBMS gibt es in verschiedene Ausführungen:

  • Stand-Alone / Desktop DB Hier liegt die DB auf einem Arbeitsplatzrechner (lokal) und es kann i.d.R. nur ein Programm (User) auf die Daten zugreifen. Typische Vertreter sind Access und Paradox.
  • File-Share DB Hier ist die Datenbank in der Lage, mehreren Anwendern gleichzeitig Zugriff auf die Daten zu gewähren. Die Daten liegen auf einem Datenserver. Der Nachteil dieses Verfahrens ist, dass für den Zugriff alle Daten vom Server auf den Client transportiert werden müssen.
  • Client-Server DB In diesem Fall kann der Arbeitsplatz (Client) nicht direkt auf die Daten zugreifen, sondern er muss eine Anforderung an den Datenbankserver schicken. Dieser bearbeitet die Anfrage und schickt die Ergebnismenge zurück an den Client. Das hat den Vorteil, dass nur die wirklich benötigten Daten zum Client gesendet werden. Normalerweise werden dann auch bestimmte Funktionen auf dem Server implementiert, die dann von allen Clients aufgerufen werden können. Bei Änderungen muss dann nur die Serversoftware (Datenbankfile) erneuert werden (auf einem PC) und nicht unbedingt die Clientsoftweare (die auf mehreren PC's installiert sein kann). Dieses Prinzip nennt man auch Fat-Server-Prinzip. Wenn ein Großteil der Arbeit vom Server erledigt wird, kann an der Hardwareausstattung der Clients gespart werden.
  • Multi-Tier DB Eine Multi-Tier DB besteht nicht nur aus 2 Ebenen wie die Client-Server DB, sondern aus 3: den Clients, dem Application-Server und dem Datenbankserver. Die Clients greifen auf den Application-Server zu, auf dem die Geschäftsprozesse und die Verbindung mit den Daten laufen. Auf dem Datenbankserver liegt die eigentliche DB. Der Client besteht dabei aus einem kleinen Programm, vielleicht einem Browser und ein Applet das einfach zu installieren ist. Solche mehrschichtigen Anwendungen sind allerdings nur für große Netzwerke interessant, da die Programmierung sehr aufwändig ist.

Relationale DB Der Begriff geht auf einen Artikel von E.F. Codd von 1970 zurück. Dort definierte Codd 333 Kriterien die eine Datenbank erfüllen muss, damit sie sich „relational" nennen darf. Keine der heutigen auf dem Markt erhältlichen DB-Systeme befolgt alle 333 Regeln. Eine Datenbank darf sich relational nennen, wenn sie die meisten dieser Bestimmungen erfüllt und der Philosophie dieses Regelwerkes gerecht wird. Relation Eine Relation ist eine Tabelle in der eine feste Anzahl von Spalten (Attribute) existiert. Diese Attribute müssen logisch zusammenhängen. Beispiel: Relation: "Person" Attribute: "Alter", "Name", "Größe", "Gewicht" Domain Eine Domain ist identisch mit einem Typ in ObjectPascal. Durch die Definition eigener Domains ist es einfach die Datenbank aufzubauen und auch zu verändern. Bei Interbase ist es wichtig Domains zu verwenden, da Interbase für jedes Attribut eine eigene Domain erzeugt. Bei 100 Attributen also 100 verschiedenen Domains. Das macht die DB unhandlich und verschwendet unnötig Speicher. Zudem können bei Domains gewisse Regeln vereinbart werden, die die eingegebenen Werte überprüfen. NULL Der Wert NULL ist ein besonderer Wert innerhalb der DB Programmierung. Wenn ein Wert unbekannt ist, wird dieser Wert verwendet. NULL ist nicht 0 oder ''! Normalisierung Daten werden in Datenbanken in Tabellen gespeichert. Nun kann man hergehen und eine Tabelle benutzen und dort alle Daten die gespeichert werden sollen reinwerfen. Das würde dann ungefähr so aussehen:

Name Anschrift Rechnung Betrag Datum
Müller Anton Testweg 1 70000 Stuttgart 1 205 15.1.2011
Müller Anton Testweg 1 70000 Stuttgart 2 26 23.2.2011

Das soll eine Tabelle darstellen in der ein Versandhaus ihre Kunden sowie deren Rechnungen speichert. Sieht eigentlich ganz OK aus. Die Probleme kommen später, aber sie kommen. Bei jeder Bestellung muss die Adresse von Hr. Müller komplett neu eingegeben werden. Bei genügend Tippfehlern weiß man irgendwann nicht mehr, welche der vielen Adressen nun richtig und welche falsch ist. Tritt Hr. Müller von diesen beiden Bestellungen zurück, werden die zugehörigen Rechnungen gelöscht. Das führt dazu, dass die Adresse von Hr. Müller verloren geht und das ist nicht im Sinne des Chefs. Das dritte Problem ist, dass bei einer Änderung der Postleitzahl von Herrn Müller, alle Datensätze einzeln durchgegangen und die Änderung bei den entsprechenden Einträgen vorgenommen werden müssen. Um alle diese Probleme zu beseitigen haben sich kluge Leute das Verfahren der Normalisierung einfallen lassen. Es gibt mehrere Stufen der Normalisierung, je höher diese ist, desto besser die Datenbank - zumindest in der Theorie. 1. Normalform: Jede Spalte einer Tabelle enthält unteilbare Informationen. Die Datensätze verwenden keine sich wiederholenden Informationen, die nicht auch zu einer separaten Gruppe zusammengefasst werden können. zu Deutsch: In der Spalte (Attribut) Name und Anschrift werden Informationen gespeichert, die teilbar sind (Name und Vorname; Straße, PLZ und Ort). Zudem wiederholt sich die Adresse bei jeder weiteren Rechnung von Hr. Müller. Die Lösung ist, die Informationen auf mehrere Tabellen zu verteilen: Tabelle Anschrift:

Name Vorname Strasse Nummer PLZ Ort
Müller Anton Testweg 1 70000 Stuttgart

Tabelle Rechnung:

Kunde_Name Kunde_Vorname RechnungsNr Betrag Datum
Müller Anton 1 205 15.01.2004

Die Verbindung der beiden Tabellen wird über so genannte Schlüssel erledigt. In unserem Fall ist dies den Name und der Vorname des Kunden. In der Tabelle "Adresse" heißen diese beiden Spalten "Primärschlüssel", da nach diesen Spalten die Tabelle geordnet und gesucht wird. Mehrere Einträge mit identischen Werten in diesen beiden Spalten sind nicht zulässig. Es ergeben sich daraus eine Menge Probleme, die so ziemlich alle durch die 2. Normalform gelöst werden: 2. Normalform: Primärschlüssel sollen nicht zusammengesetzt sein. Also kann als Primärschlüssel für die Tabelle Adresse nicht der Name und Vorname verwendet werden. Der Name allein bringt nichts, der Vorname ebenfalls nicht. Es muss also ein Ordnungskriterium her, das eindeutig ist: Eine Zahl, die Kundennummer. Es hat sich gezeigt, dass, unabhängig was letztendlich in einer Tabelle gespeichert wird, eine fortlaufende Nummer, die eigentlich nichts mit dem Inhalt der Tabelle zu tun hat, als Primärschlüssel das geeignetste ist (Ausnahmen gibt es natürlich auch) 3. Normalform: Es sind keine funktionale Abhängigkeiten zwischen Spalten erlaubt, die nicht als Primärschlüssel definiert sind. Was ist das nun wieder? In der Tabelle "Adresse" ist eine solche Abhängigkeit vorhanden: PLZ und Ort! Es gibt zu jeder PLZ einen fest definierten Ort, also ist der Ort von der PLZ abhängig. Das "Problem" kann man so lösen, dass man den Ort in einer weiteren Tabelle "auslagert" und in der Tabelle Adresse lediglich die PLZ stehen lässt. Hier kommt allerdings mein Einwurf von oben ins Spiel [Es gibt mehrere Stufen der Normalisierung, je höher diese ist, desto besser die Datenbank - zumindest in der Theorie.] Wenn der Ort ausgelagert ist, werden Abfragen die die Adresse eines bestimmten Kunden suchen sollen, wesentlich aufwändiger. Eine Auslagerung würde nur dann Sinn machen, wenn eine Liste mit PLZ und den dazugehörenden Orten vorhanden ist bzw. aufgebaut werden soll. Es gibt noch weitere Stufen der Normalisierung, die aber i.d.R. nur theoretischen Wert besitzen. Bei umfangreichen Datenbanken könnten diese evtl. eine Rolle spielen, doch wie oben beschrieben, wird lieber nach einer einfachen und schnellen Auswertung optimiert als nach der höchsten Stufe der Normalisierung. Unser Beispiel würde nach allem so aussehen: Tabelle Anschrift:

KundeNr Name Vorname Strasse Nummer PLZ Ort
1 Müller Anton Testweg 1 70000 Stuttgart

Tabelle Rechnung:

Kunde_Nr RechnungsNr Betrag Datum
1 1 205 15.01.2004

Schlüssel (Key) Schon bei der Normalisierung habe ich die Primärschlüssel etwas erläutert. es gibt neben den Primärschlüsseln auch noch Fremdschlüssel Primärschlüssel: Primärschlüssel sind dafür verantwortlich einen Datensatz in einer Tabelle eindeutig zu identifizieren. Es darf kein anderer Datensatz dieser Tabelle einen gleichwertigen Primärschlüssel besitzen. Jede Tabelle sollte einen solchen Primärschlüssel besitzen um schnell auf die Daten zugreifen zu können, da die DBMS einen Index für diese Schlüssel erstellen. Fremdschlüssel: Fremdschlüssel sind Primärschlüssel einer anderen Tabelle. In unserem Beispiel ist die Spalte Kunden_Nr ein Fremdschlüssel in der Tabelle "Rechnungen". Fremdschlüssel dienen dazu verschiedene Tabellen miteinander zu verbinden. Es gibt verschiedene Regelungen wie Fremdschlüssel reagieren sollen, z.B. wenn der zugehörige Eintrag aus der übergeordneten Tabelle gelöscht wird. (Es wird also Hr. Müller aus der Adresstabelle gelöscht. Was soll mit den Rechnungen geschehen?). Eigentlich sollte in einem solchen Fall die Löschung der Daten nicht erlaubt werden und eine Fehlermeldung ausgegeben werden, da sonst evtl. wichtige Daten verloren gehen können. Man kann auch vereinbaren, dass in einem solchen Fall, die Daten der untergeordneten Tabelle (also "Rechnungen") ebenfalls gelöscht werden sollen - man sollte aber genau wissen was man macht! Für die Fremdschlüssel wird ebenfalls ein Index erzeugt, um schnelle Abfragen zu ermöglichen. Index Ein Index ist ein Suchregister oder eine Inhaltsangabe was alles in einer Tabelle steht. Dabei wird der Index auf eine besondere Art gespeichert. Die Daten werden nicht sequentiell (nacheinander) gespeichert, sondern so, dass schnell auf diese Daten zugegriffen werden kann. Auch mit dem Nachteil, dass dafür mehr Speicher benötigt wird. Die Leistungsfähigkeit eines Index ist für die Datenbank entscheidend, deshalb gibt es in allen großen DBMS Funktionen, die einen Index neu aufbauen. Alle Spalten, die Informationen beinhalten nach denen später gesucht werden muss, sollten einen solchen Index besitzen. Um wieder auf unser Beispiel zurückzukommen: es wäre sicherlich sinnvoll für den Nachnamen einen Index zu erzeugen um die Suche nach dem Namen (vielleicht auch Ort, PLZ, Straße) zu beschleunigen. Zu freigiebig sollte man mit der Erzeugung von Indizes allerdings auch nicht sein. Je mehr Indizes gepflegt werden müssen desto länger benötigen Speicherung, Änderung und Löschung von Daten. Bei sehr umfangreichen Datenbeständen wird oft hergegangen und eine Datenbank mit wenigen Indizes für die Pflege der Daten und eine zweite Datenbank mit vielen Indizes für die Auswertung geführt. Das bedeutet aber auch, dass die Daten in der "Auswertungsdatenbank" nicht aktuell sind! Das Entity-Relationship-Modell (ER-Diagramm) Das ER-Diagramm ist eine Hilfe um umfangreiche Aufgabenstellungen einfach und schnell in ein Datenbankmodell zu bekommen. Zudem erhält man die Möglichkeit schon lange bevor irgend eine Zeile Code geschrieben worden ist, seinen Entwurf zu testen (zumindest von Hand). Jeder hat sicherlich schon mal Änderungen an einem Programm vornehmen müssen, da ein Umstand eingetreten ist, den er vorher nicht bedacht hat oder der einfach noch nicht ersichtlich war. Bei einem Programm geht das noch recht gut (obwohl da teilweise ein erheblicher Aufwand benötigt wird). Bei einer Datenbank hat man in einem solchen Fall sehr viel Arbeit. Wenn umfangreiche Umbauten an der Struktur anstehen, taucht auch das Problem auf, wie die vorhandenen Daten in das neue Modell passen und wenn sie reinpassen, müssen die in die neue Struktur auch eingespielt werden und das dauert! Das ER-Diagramm enthält Entities (Objekte wie z.B. Kunde, Rechnung, also letztendlich die späteren Tabellen) und Relationen also Beziehungen der Objekte untereinander. Dieses wird grafisch dargestellt (da gibt es sogar eine DIN oder ISO dafür). Es gibt eine feste Anzahl von Beziehungen die die Objekte miteinander eingehen können:

  • 1:1 Eins-zu-Eins Beziehung: z.B. ein Kunde hat eine Adresse
  • 1:N Eins-zu-viele Beziehung: z.B. Ein Kunde hat mehrere Rechnungen
  • N:M viele-zu-viele Beziehung: z.B. Es gibt viele Rechnungen die mehrere Artikel beinhalten und Artikel können in mehreren Rechnungen auftauchen

Es gibt jetzt aber eine Erweiterung des ganzen, da (die 1:N Beziehung betrachtet) ein Kunde auch mal keine Rechnung zugewiesen bekommt, da er z.B. seine erste Bestellung widerrufen hat. Deshalb gibt es das erweitere ER-Diagramm, indem die Beziehungen auch so aussehen können: 1:0/N: also die eines-zu-keines-oder-vielen Beziehung: Ein Kunde kann keine oder mehrere Rechnungen besitzen (den Fall, dass ein Kunde eine Rechnung besitzt soll in diesem Beispiel einfach mal verschwiegen werden). Mit dieser keine-Beziehung können alle oberen Beziehungen erweitert werden. Bauen wir uns aus einem Beispiel einfach mal ein solches ER-Diagramm und die Datenbank zusammen. Die Aufgabe: Es soll eine Datenbank entwickelt werden, in der ein Versandhaus die Kundendaten, die Bestellungen, die Artikel sowie die Rechnungen gespeichert werden können. Zuerst suchen wir die Entities raus, in diesem Fall sehr einfach: Kunde, Bestellung, Artikel, Rechnung (einfach die Substantive in der Aufgabe suchen). Auch das Aufstellen der Beziehungen sollte nicht allzu schwer sein: ERDiagramm Erklärung: Ein Kunde kann keine oder mehrere Bestellungen und Rechnungen besitzen. In einer Bestellung können eine oder mehrere Artikel enthalten sein, ein Artikel aber auch in mehreren Bestellungen. Hier käme jetzt das Testen und Überprüfen des ER-Diagramms aufgrund der Anforderungen die der Kunde an die bestehende Anwendung hat. Das können bestimmte Auswertungen sein, die der Kunde benötigt oder auch Einschränkungen die das DBMS/ die Entwicklungsumgebung das/die verwendet werden soll, mitbringt. Jetzt entwickeln wir aus diesem Diagramm die eigentlichen Tabellen. Die Tabelle Kunde und Bestellung können wir von oben abschreiben: Tabelle Anschrift:

IDth> Name Vorname Strasse Nummer PLZ Ort

Tabelle Rechnung:

ID Kunde_ID RechnungsNr Betrag Datum

(ID Primärschlüssel; Kunde_ID Fremdschlüssel) Damit sollte auch klar sein, wie eine 1:0/N Beziehung in eine Tabelle verwandelt wird. In der Tabelle mit der "0/N" wird der Primärschlüssel der 1er-Tabelle als Fremdschlüssel definiert. Damit ist die Tabelle Bestellung auf kein Problem:

ID Kunde_ID BestellNr Datum

Allerdings wurde in der Beschreibung klar gesagt, dass in einer Bestellung mehrere Artikel stehen sollen ein Artikel aber auch in mehreren Bestellungen - wo soll nun welcher Primärschlüssel hin? Ganz einfach: M:N Beziehungen sind über Verbindungstabellen aufzulösen: Artikel:

ID ArtikelNr Beschreibung Preis

Artikel_Bestellung:

Bestellung_ID Artikel_ID Menge

In der Tabelle Artikel_Bestellung werden die Artikelnummer sowie die zugehörige Bestellnummer gespeichert, wenn der Artikel geordert wird. Zudem wird in unserem Beispiel die Menge des Artikels gespeichert. Wer aufgepasst hat stellt fest, dass die Tabellen ziemlich ähnlich aufgebaut sind. Zum einen sind die Tabellen immer mit der Einzahl des Begriffes benannt, der in ihnen gespeichert wird, z.B. "Kunde" und nicht "Kunden". Der Primärschlüssel wird immer mit "ID" bezeichnet, die Fremdschlüssel immer mit "Tabellenname_ID", also "Kunde_ID". Das ganze habe ich nicht willkürlich so gemacht, sondern folgt einem "Quasi-Standard" in der DB-Entwicklung. So muss bei der Arbeit mit der Datenbank nicht überlegt werden, wie der Fremdschlüssel für die Kunden in der Tabelle "Rechnung" heißt und was in der Spalte "Kunde.Name" gespeichert ist. Das ganze hört sich jetzt sicherlich lächerlich an, aber genauso wie die Kommentare in einem Quellcode wichtig sind, ist die Einhaltung eines Standards in der DB-Entwicklung wichtig. So nun würde eine weitere Prüfung sowie Tests folgen, ob das Ergebnis wirklich das leistet, was verlangt wird. In diesem Stadium können auch schon Abfragen entwickelt werden, ob die Daten auch wieder herauskommen, die in der Datenbank gespeichert werden. Hat das Modell alle Prüfungen bestanden würde die Umsetzung in die Datenbank und anschließen die Erstellung des Programms erfolgen. Da die Umsetzung in die eigentliche Datenbank vom verwendeten DBMS abhängt, ist eine allgemeingültige Lösung nicht möglich. Wer sich dafür Interessiert, sei auf das Interbase-SQL Tutorial verwiesen, in dem ich dieses Beispiel quasi "vollende". Quellen: Delphi 5 Datenbankprogrammierung, Michael Ebner im Addison-Wesley-Verlag ISBN 3-8273-1570-0 Interbase Datenbankentwicklung mit Delphi, Andreas Kosch im Software & Support Verlag, ISBN 3-935042-09-4