Der 70-Tabellen Wahnsinn: Eine Kontaktdatenbank im Normalisierungs-Rausch
Oder: Wie man aus einer simplen Adressverwaltung ein akademisches Meisterwerk der Über-Architektur erschafft*
Ein gefährliches Gedankenspiel
Heute Morgen saß ich am Schreibtisch, starrte auf meine FileMaker-Lösung und dachte mir: “Wäre es nicht herrlich, einmal komplett durchzudrehen?” Nicht im Sinne von schlechtem Code oder wilden Skripten, sondern im Sinne von theoretischer Perfektion. Von akademischer Reinheit. Von Normalisierung, die so weit geht, dass selbst Edgar F. Codd sich im Grab umdreht und fragt: “Habe ich das wirklich so gemeint?”
Heute nehme ich euch mit auf eine Reise in die dunkle Seite der Datenbankentwicklung. Wir werden gemeinsam eine Kontaktdatenbank bauen, die so übernormalisiert ist, dass sie in der Praxis völlig unbrauchbar wäre. Wir sprechen von siebzig Tabellen. Siebzig! Für Kontakte, Adressen und ein bisschen Drumherum. Das ist ungefähr so, als würde man einen Nagel mit einem Vorschlaghammer einschlagen, während man auf einem Einrad balanciert und dabei Schach spielt.
Vorweg: Das solltet ihr niemals tun. Aber es macht wahnsinnig viel Spaß, und man lernt dabei eine Menge über Normalisierung, Beziehungen und darüber, wann genug wirklich genug ist.
Die Ausgangslage: Eine ganz normale Kontaktdatenbank
Stellen wir uns vor, wir wollen eine Kontaktdatenbank bauen. Nichts Wildes. Einfach Namen, Adressen, Telefonnummern, E-Mails. Das Zeug, das jeder braucht. In der Praxis würde man vielleicht mit drei, vier Tabellen davonkommen. Kontakte, Adressen, Telefonnummern, E-Mails. Fertig. Schön übersichtlich, performant, wartbar.
Aber wo bleibt da der Spaß? Wo ist die theoretische Eleganz? Wo sind die Normalformen, von denen alle reden, die aber keiner wirklich konsequent durchzieht? Genau hier fängt unser Abenteuer an.
Der Plan: Maximale Normalisierung
Die Idee ist simpel: Wir normalisieren alles, was nicht bei drei auf den Bäumen ist. Jede noch so kleine Information bekommt ihre eigene Tabelle. Jede Redundanz wird gejagt und eliminiert. Jede transitive Abhängigkeit wird aufgelöst. Wir gehen nicht nur bis zur dritten Normalform, nicht mal bis zur BCNF. Wir gehen bis zur fünften, vielleicht sogar sechsten Normalform, wenn wir wirklich übermütig werden.
Das Ergebnis? Eine Datenbank-Struktur, die aussieht wie ein Spinnennetz nach einem Tornado. Oder wie der Stammbaum der britischen Königsfamilie. Oder wie mein Schreibtisch nach einer langen Entwicklungssession.
Die Kontakte: Wo alles beginnt
Fangen wir harmlos an. Ein Kontakt hat einen Vornamen, einen Nachnamen, vielleicht ein Geburtsdatum. In einer normalen Welt würden wir das in eine Tabelle schreiben und fertig. Aber wir sind ja nicht normal.
Ein Kontakt hat eine Anrede. Herr, Frau, Divers. Das kann sich ändern. Das muss zentral verwaltet werden. Also brauchen wir eine Anreden-Tabelle. Klingt noch vernünftig, oder? Gut, dann machen wir weiter.
Ein Kontakt kann einen Titel haben. Dr., Prof., Dipl.-Ing. und all die schönen Dinge, mit denen Menschen sich schmücken. Aber hier wird es spannend: Manche Titel stehen vor dem Namen, manche danach. In Deutschland sagt man “Dr. Müller”, in Österreich manchmal “Müller, Dr.”. Das ist eine Information! Eine strukturierte Information! Also brauchen wir nicht nur eine Titel-Tabelle, sondern auch eine TitelPositionen-Tabelle. Zwei Tabellen, nur damit wir wissen, ob der Doktortitel vor oder nach dem Namen kommt. Herrlich übertrieben, oder?
Die Firmen: Ein Exkurs in die Unternehmensarchitektur
Kontakte arbeiten in Firmen. Das ist nichts Neues. Aber eine Firma ist nicht einfach nur ein Name. Eine Firma hat eine Rechtsform. GmbH, AG, e.K., OHG, KG, und was weiß ich noch alles. Die Rechtsform sagt etwas über die Struktur aus, über Haftung, über steuerliche Behandlung. Das ist wichtig! Also brauchen wir eine Rechtsformen-Tabelle.
Und dann ist da noch die Branche. IT, Maschinenbau, Einzelhandel, Gastronomie. Branchen ändern sich, Branchen haben Hierarchien, Branchen sind komplex. Also brauchen wir eine Branchen-Tabelle. Vielleicht sogar mit Hierarchien, aber das würde jetzt zu weit führen. Wir haben ja erst bei Tabelle Nummer acht oder so.
Und weil ein Kontakt bei mehreren Firmen arbeiten kann, gleichzeitig oder nacheinander, brauchen wir natürlich eine Verknüpfungstabelle. Kontakt_Firma. Mit Position (Geschäftsführer, Angestellter, Freelancer), mit von-Datum und bis-Datum für die Historisierung. Und die Position? Rate mal. Eigene Tabelle. Positionen-Tabelle. Weil sich Positionsbezeichnungen ändern können und wir sie zentral verwalten wollen.
Die Adressen: Ein geografisches Meisterwerk
Jetzt wird es richtig lustig. Adressen sind kompliziert. Wirklich kompliziert. Eine Adresse besteht aus einer Straße, einer Hausnummer, einer Postleitzahl, einem Ort, einem Land. Das würde die meisten zufriedenstellen. Aber nicht uns. Oh nein.
Fangen wir mit der Straße an. Eine Straße hat einen Namen. Aber eine Straße gehört zu einem Ort. Und verschiedene Orte können Straßen mit dem gleichen Namen haben. Die Hauptstraße in Berlin ist nicht die Hauptstraße in München. Also brauchen wir eine Strassen-Tabelle, die zu einem Ort gehört. Schon haben wir eine Redundanz eliminiert.
Die Postleitzahl? Die gehört zur Adresse, nicht zum Ort! Ein großer Ort kann mehrere Postleitzahlen haben. Berlin hat hunderte davon. Also brauchen wir eine PLZ-Tabelle, die wiederum zu einem Ort gehört. Moment, aber eine PLZ kann sich auch über mehrere Orte erstrecken? Stimmt! Also machen wir eine n:m-Beziehung daraus. Ups, schon wieder komplizierter.
Orte gehören zu Regionen. Bundesländer, Kantone, Staaten, je nachdem wo wir sind. Also Regionen-Tabelle. Und Regionen gehören zu Ländern. Also Länder-Tabelle. Und Länder haben ISO-Codes, zweibuchstabig und dreibuchstabig, und Ländervorwahlen für Telefonnummern. Und eine Währung! Also brauchen wir auch noch eine Währungen-Tabelle.
Ach, und ich habe fast die Adresszusätze vergessen. “c/o”, “bei”, “℅”. Das sind strukturierte Informationen! Adresszusätze-Tabelle. Und natürlich brauchen wir eine AdressTypen-Tabelle, denn eine Adresse kann privat, geschäftlich, eine Lieferadresse oder eine Rechnungsadresse sein.
Und weil sowohl Kontakte als auch Firmen mehrere Adressen haben können, brauchen wir Verknüpfungstabellen. Kontakt_Adresse und Firma_Adresse. Mit einem Flag, welche die Hauptadresse ist. Und mit Gültigkeitsdaten, denn Menschen und Firmen ziehen um.
Wir sind jetzt bei ungefähr zwanzig Tabellen. Nur für Kontakte, Firmen und Adressen. Und wir haben noch nicht einmal über Telefonnummern gesprochen.
Die Telefonnummern: Eine Ode an die Verkomplizierung
Telefonnummern sind toll. Man könnte sie einfach als String speichern und gut ist. Aber wo bleibt da die Struktur? Wo ist die Eleganz?
Eine Telefonnummer besteht aus einer Ländervorwahl, einer Ortsvorwahl, einer Rufnummer und vielleicht einer Durchwahl. Das sind vier Felder! Vier strukturierte Informationen! Also brauchen wir eine Telefonnummern-Tabelle mit all diesen Feldern.
Aber die Ländervorwahl? Die gehört zu einem Land! Also brauchen wir eine Ländervorwahlen-Tabelle, die zur Länder-Tabelle verlinkt. Ja, wir haben die Ländervorwahl schon in der Länder-Tabelle gespeichert, aber das wäre ja eine Redundanz! Also machen wir eine eigene Tabelle daraus, damit wir sauber bleiben.
Und dann ist da noch der Typ. Mobil, Festnetz, Fax, VoIP. Das sind strukturierte Kategorien. TelefonTypen-Tabelle. Natürlich.
Und weil wieder sowohl Kontakte als auch Firmen Telefonnummern haben können, brauchen wir Verknüpfungstabellen. Kontakt_Telefon und Firma_Telefon. Mit einem Flag für die Hauptnummer. Mit Gültigkeitsdaten. Ihr kennt das Spiel.
E-Mails, Websites und der Rest: Wir sind noch nicht fertig
E-Mail-Adressen? Eigene Tabelle. Mit einem EmailTyp (privat, geschäftlich, Newsletter). EmailTypen-Tabelle. Und wieder Verknüpfungstabellen für Kontakte und Firmen. Kontakt_Email, Firma_Email.
Websites? Eigene Tabelle. Mit einem WebsiteTyp (Hauptwebsite, Blog, LinkedIn, XING, Facebook, Instagram, TikTok, was auch immer). WebsiteTypen-Tabelle. Und wieder Verknüpfungstabellen. Ihr ahnt es schon.
Bankverbindungen? IBAN, BIC, und eine Referenz zur Bank. Und die Bank? Eigene Tabelle natürlich! Mit Bankname, BLZ und einer Referenz zum Land. Und wieder Verknüpfungstabellen für Kontakte und Firmen.
Steuer-IDs? Eigene Tabelle! Mit einem SteuerTyp (USt-ID, Steuernummer, UID, je nach Land). SteuerTypen-Tabelle. Die wiederum eine Referenz zum Land hat, weil verschiedene Länder verschiedene Steuertypen haben. Und wieder Verknüpfungstabellen.
Wir sind jetzt irgendwo bei vierzig, fünfzig Tabellen. Und ich habe noch ein paar Ideen.
Die Kür: Beziehungen, Sprachen und Tags
Kontakte haben Beziehungen zueinander. Ehepartner, Kinder, Geschwister, Geschäftspartner, Freunde. Das ist eine klassische n:m-Beziehung. Also brauchen wir eine Beziehungen-Tabelle, die zwei Kontakte miteinander verbindet. Und natürlich einen BeziehungsTyp. BeziehungsTypen-Tabelle. Mit einem Datum, seit wann die Beziehung besteht. Man will ja dokumentieren, wann man geheiratet hat oder wann die Geschäftsbeziehung begann.
Kontakte sprechen Sprachen. Und Firmen kommunizieren in Sprachen. Also brauchen wir eine Kommunikationssprachen-Tabelle, die entweder zu einem Kontakt oder zu einer Firma gehört. Und diese Tabelle verweist auf eine Sprachen-Tabelle mit dem Sprachennamen und ISO-Code. Und auf eine Sprachlevel-Tabelle (Muttersprache, Fließend, Grundkenntnisse), weil das Level der Sprachkenntnisse relevant ist.
Und dann sind da noch Tags. Kategorien. Labels. Wie auch immer man sie nennen will. Kontakte und Firmen können mit Tags versehen werden, um sie zu gruppieren, zu filtern, zu organisieren. Also brauchen wir eine Tags-Tabelle. Und weil Tags selbst wieder Kategorien haben können (Marketing-Tags, Projekt-Tags, Status-Tags), brauchen wir eine TagKategorien-Tabelle. Und natürlich Verknüpfungstabellen. Kontakt_Tag und Firma_Tag.
Jetzt sind wir bei siebzig Tabellen. Siebzig! Für eine Kontaktdatenbank!
Das Diagramm: Visuelle Überforderung garantiert
An dieser Stelle sollte ich eigentlich ein Diagramm zeigen, das all diese Tabellen und ihre Beziehungen visualisiert. Aber ehrlich gesagt, das würde diesen Blog-Post sprengen. Ich habe das Diagramm erstellt. Es ist ein Monster. Es sieht aus wie ein Spinnennetz auf Steroiden. Es hat mehr Linien als ein U-Bahn-Plan von Tokio. Es ist wunderschön und schrecklich zugleich.
Wenn ihr das Diagramm sehen wollt, könnt ihr es euch als Mermaid-Datei herunterladen und in einem Tool eurer Wahl anschauen. Ich empfehle mermaid.live, denn dann könnt ihr ordentlich hinein- und herauszoomen. Ihr werdet es brauchen. Glaubt mir.
Das Diagramm zeigt jede einzelne Tabelle mit ihren Attributen, ihren Primary Keys, ihren Foreign Keys. Es zeigt jede Beziehung, jede Kardinalität, jeden Join. Es ist ein akademisches Meisterwerk. Und es ist in der Praxis völlig wahnsinnig.
Die Realität: Warum niemand das tun sollte
Jetzt fragt ihr euch wahrscheinlich: “Warum zum Teufel erzählst du uns das alles, wenn es doch sowieso niemand machen sollte?” Gute Frage. Die Antwort ist zweiteilig.
Erstens: Weil es wichtig ist zu verstehen, wie weit Normalisierung theoretisch gehen kann. Normalisierung ist ein mächtiges Werkzeug. Sie eliminiert Redundanzen, sie sorgt für Datenintegrität, sie macht Updates und Löschungen sicher. Das sind alles gute Dinge. Aber wie bei jedem Werkzeug gibt es einen Punkt, an dem man es übertreibt. Einen Punkt, an dem die Nachteile die Vorteile überwiegen.
Unsere siebzig-Tabellen-Kontaktdatenbank ist so ein Punkt. Sie ist theoretisch perfekt. Sie hat keine Redundanzen. Jede Information ist genau einmal gespeichert. Änderungen an Lookup-Daten wirken sich automatisch überall aus. Historisierung ist eingebaut. Alles ist sauber, strukturiert, elegant.
Aber sie ist auch ein Performance-Albtraum. Um einen einzelnen Kontakt mit allen Details anzuzeigen, müsste man zwanzig, dreißig Tabellen joinen. Jede Abfrage würde ewig dauern. Indizes könnten nur bedingt helfen, weil die Datenbank einfach zu fragmentiert ist. Das Caching würde zum Problem, weil jede kleine Änderung potentiell Dutzende von Caches invalidiert.
Und dann ist da die Wartbarkeit. Stellt euch vor, ihr müsst in diese Struktur eine neue Telefonnummer eintragen. Ihr müsst die Telefonnummer-Tabelle befüllen, die Ländervorwahl nachschlagen, den Telefontyp auswählen, die Verknüpfungstabelle befüllen, das Hauptnummer-Flag setzen. Das sind mindestens fünf verschiedene Operationen, die alle in einer Transaktion laufen müssen. Ein Fehler, und alles ist inkonsistent.
Und die Komplexität für den Entwickler! Jeder neue Entwickler, der in dieses Projekt kommt, würde eine Woche brauchen, nur um die Struktur zu verstehen. Dokumentation? Ja, die wäre nötig. Sehr viel Dokumentation. Und selbst dann würde man ständig nachschauen müssen, welche Tabelle jetzt für was zuständig ist.
Zweitens: Weil es Spaß macht. Ernsthaft. Es macht Spaß, mal theoretisch bis zum Äußersten zu gehen. Es macht Spaß, ein Gedankenexperiment durchzuziehen und zu sehen, wo es hinführt. Und es macht Spaß, am Ende zu sagen: “Okay, das war interessant, aber in der Praxis machen wir es anders.”
Wo liegt die Balance?
Die Frage, die wir uns stellen sollten, ist nicht “Wie weit kann ich normalisieren?”, sondern “Wie weit sollte ich normalisieren?”. Und die Antwort darauf ist, wie so oft in der Softwareentwicklung: Es kommt darauf an.
Für eine Kontaktdatenbank in der Praxis würde ich wahrscheinlich mit zehn bis fünfzehn Tabellen arbeiten. Kontakte, Firmen, Adressen, Telefonnummern, E-Mails, vielleicht Websites. Dazu ein paar Lookup-Tabellen für Länder, Adresstypen, Telefontypen. Vielleicht eine Verknüpfungstabelle für die Kontakt-Firma-Beziehung, wenn das Anforderungen erfordern.
Aber ich würde nicht jede kleinste Information in eine eigene Tabelle auslagern. Ich würde Ortsvorwahlen nicht von der Telefonnummer trennen. Ich würde die Ländervorwahl direkt in der Länder-Tabelle speichern, nicht in einer separaten Tabelle. Ich würde Adresszusätze als Freitextfeld behandeln, nicht als Lookup.
Warum? Weil die Balance wichtig ist. Balance zwischen Normalisierung und Performance. Balance zwischen Datenintegrität und Wartbarkeit. Balance zwischen theoretischer Eleganz und praktischer Brauchbarkeit.
Normalisierung ist kein Selbstzweck. Sie ist ein Mittel zum Zweck. Der Zweck ist eine funktionierende, wartbare, performante Datenbank. Wenn Normalisierung diesen Zweck unterstützt, ist sie gut. Wenn sie ihm im Weg steht, muss man einen Schritt zurücktreten und denormalisieren.
FileMaker-spezifische Überlegungen
Als FileMaker-Entwickler haben wir noch ein paar zusätzliche Dinge zu bedenken. FileMaker ist kein klassisches SQL-Datenbanksystem. Es hat seine eigenen Stärken und Schwächen, seine eigenen Paradigmen.
FileMaker ist visuell. Beziehungen werden im Beziehungsgraphen dargestellt. Ein Beziehungsgraph mit siebzig Tabellen? Das ist kein Graph mehr, das ist ein Chaos. Man würde den Überblick verlieren. Man würde sich permanent verirren. Man würde neue Beziehungen übersehen oder alte falsch verknüpfen.
FileMaker ist portalbasiert. Wenn wir Daten aus verknüpften Tabellen anzeigen wollen, nutzen wir Portale. Aber Portale haben Grenzen. Man kann nicht beliebig tief graben. Jedenfalls nicht ohne verrückt zu werden. Man kann nicht beliebig viele Joins machen, ohne dass die Performance leidet. Eine übernormalisierte Struktur würde uns zwingen, Portale in Portalen zu verwenden (geht nicht), verschachtelte Selects, komplexe Berechnungen. Das würde langsam werden. Sehr langsam.
Und dann ist da noch die Frage der Wertelisten. In unserer übernormalisierten Datenbank haben wir Dutzende von Lookup-Tabellen. AdressTypen, TelefonTypen, EmailTypen, und so weiter. In FileMaker würde man viele davon einfach als Wertelisten implementieren. Wertelisten sind schnell, einfach zu warten, und für die meisten Fälle völlig ausreichend. Man braucht keine eigene Tabelle für drei Werte.
Das heißt nicht, dass Normalisierung in FileMaker unwichtig wäre. Ganz im Gegenteil. Aber es heißt, dass man die FileMaker-spezifischen Werkzeuge und Konzepte berücksichtigen muss. Man muss pragmatisch sein. Man muss die Sprache sprechen, die FileMaker versteht.
Die Lehren: Was wir mitnehmen
Was lernen wir aus diesem Wahnsinn? Was nehmen wir mit aus unserem Ausflug in die Über-Normalisierung?
Erstens: Normalisierung ist wichtig. Sie sorgt für Datenintegrität, sie eliminiert Redundanzen, sie macht unsere Datenbanken robuster. Wir sollten sie nicht ignorieren, nicht aus Bequemlichkeit, nicht aus Unwissenheit.
Zweitens: Normalisierung ist kein Dogma. Es gibt Situationen, in denen Denormalisierung die richtige Wahl ist. Wenn Performance kritisch ist, wenn die Daten sowieso nicht oft geändert werden, wenn die zusätzliche Komplexität den Nutzen übersteigt. Man muss abwägen, Fall für Fall, Tabelle für Tabelle.
Drittens: Theorie und Praxis sind zwei verschiedene Dinge. Was in einem Lehrbuch steht, was in einem Universitätskurs vermittelt wird, muss nicht zwangsläufig eins zu eins in die Praxis übertragbar sein. Man muss verstehen, warum die Theorie so ist, wie sie ist. Und dann muss man entscheiden, wie man sie anwendet.
Viertens: Es macht Spaß, mal über die Stränge zu schlagen. Es macht Spaß, Grenzen auszutesten, Extreme zu erkunden, Gedankenexperimente durchzuziehen. Solange man am Ende weiß, dass es ein Experiment war, und solange man daraus lernt.
Und fünftens: Siebzig Tabellen für eine Kontaktdatenbank sind wirklich, wirklich zu viel. Bitte, bitte macht das nicht in Produktion. Ich bin nicht verantwortlich für die Konsequenzen.
Fazit: Die Kunst des Maßhaltens
Am Ende des Tages ist Datenbankdesign eine Kunst. Es ist kein strikter Prozess, kein Algorithmus, den man einfach abarbeiten kann. Es ist eine Mischung aus Wissen, Erfahrung, Intuition und gesundem Menschenverstand.
Unsere siebzig-Tabellen-Kontaktdatenbank ist ein akademisches Meisterwerk. Sie ist theoretisch perfekt. Sie ist auch praktisch unbenutzbar. Und das ist okay. Denn sie hat uns etwas gelehrt. Sie hat uns gezeigt, wie weit man gehen kann. Und damit hat sie uns auch gezeigt, wie weit man nicht gehen sollte.
Wenn ihr das nächste Mal vor einem Datenbankdesign sitzt, denkt an diese Geschichte. Denkt an die siebzig Tabellen. Lacht ein bisschen. Und dann macht es besser. Macht es praktisch. Macht es wartbar. Macht es so, dass ihr in einem Jahr noch versteht, was ihr euch dabei gedacht habt.
Normalisiert eure Datenbanken. Aber normalisiert sie mit Maß. Findet den Sweet Spot zwischen Theorie und Praxis. Und vor allem: Habt Spaß dabei. Denn am Ende des Tages sollte Entwicklung nicht nur funktional sein, sondern auch ein bisschen Freude bereiten.
Und wenn euer Chef das nächste Mal fragt, warum die Kontaktdatenbank nur zehn Tabellen hat und nicht mehr, zeigt ihm diesen Blog-Post. Sagt ihm, dass ihr wisst, wie es theoretisch geht. Aber dass ihr euch bewusst dagegen entschieden habt. Weil ihr pragmatisch seid. Weil ihr weise seid. Weil ihr keine siebzig Tabellen wollen.
Danke fürs Lesen. Und denkt dran: Normalisierung ist gut. Aber zu viel Normalisierung ist wie zu viel Koffein. Irgendwann zittert man nur noch und fragt sich, wie man hier gelandet ist.
Über z.B. https://mermaid.live könnt Ihr diesen Code Visuell darstellen lassen.
… erDiagram %% ======================================== %% KERN: KONTAKTE & PERSONEN %% ======================================== Kontakte { int KontaktID PK int AnredeID FK int TitelID FK string Vorname string Nachname date Geburtsdatum text Notizen }
Anreden {
int AnredeID PK
string Anrede
}
Titel {
int TitelID PK
string Titel
int TitelPositionID FK
}
TitelPositionen {
int TitelPositionID PK
string Position
}
%% ========================================
%% FIRMEN
%% ========================================
Firmen {
int FirmaID PK
string Firmenname
int RechtsformID FK
int BrancheID FK
}
Rechtsformen {
int RechtsformID PK
string Rechtsform
}
Branchen {
int BrancheID PK
string Branche
}
Kontakt_Firma {
int Kontakt_FirmaID PK
int KontaktID FK
int FirmaID FK
int PositionID FK
date von_Datum
date bis_Datum
}
Positionen {
int PositionID PK
string Positionsbezeichnung
}
%% ========================================
%% ADRESSEN & ORTE
%% ========================================
Adressen {
int AdresseID PK
int StrasseID FK
string Hausnummer
string HausnummerZusatz
int AdresszusatzID FK
int PLZID FK
int AdressTypID FK
}
Strassen {
int StrasseID PK
string Strassenname
int OrtID FK
}
PLZ {
int PLZID PK
string PLZ
int OrtID FK
}
Orte {
int OrtID PK
string Ortsname
int RegionID FK
}
Regionen {
int RegionID PK
string Regionsname
int LandID FK
}
Laender {
int LandID PK
string Laendername
string ISO2
string ISO3
string Laendervorwahl
int WaehrungID FK
}
Waehrungen {
int WaehrungID PK
string Waehrung
string ISO_Code
string Symbol
}
Adresszusaetze {
int AdresszusatzID PK
string Zusatz
}
AdressTypen {
int AdressTypID PK
string Typ
}
Kontakt_Adresse {
int Kontakt_AdresseID PK
int KontaktID FK
int AdresseID FK
bool IstHauptadresse
date Gueltig_von
date Gueltig_bis
}
Firma_Adresse {
int Firma_AdresseID PK
int FirmaID FK
int AdresseID FK
bool IstHauptsitz
date Gueltig_von
date Gueltig_bis
}
%% ========================================
%% TELEFON
%% ========================================
Telefonnummern {
int TelefonID PK
int LaendervorwahlID FK
string Ortsvorwahl
string Rufnummer
string Durchwahl
int TelefonTypID FK
}
Laendervorwahlen {
int LaendervorwahlID PK
string Vorwahl
int LandID FK
}
TelefonTypen {
int TelefonTypID PK
string Typ
}
Kontakt_Telefon {
int Kontakt_TelefonID PK
int KontaktID FK
int TelefonID FK
bool IstHauptnummer
date Gueltig_von
date Gueltig_bis
}
Firma_Telefon {
int Firma_TelefonID PK
int FirmaID FK
int TelefonID FK
string Abteilung
date Gueltig_von
date Gueltig_bis
}
%% ========================================
%% E-MAIL
%% ========================================
EMail_Adressen {
int EmailID PK
string EmailAdresse
int EmailTypID FK
}
EmailTypen {
int EmailTypID PK
string Typ
}
Kontakt_Email {
int Kontakt_EmailID PK
int KontaktID FK
int EmailID FK
bool IstHauptemail
date Gueltig_von
date Gueltig_bis
}
Firma_Email {
int Firma_EmailID PK
int FirmaID FK
int EmailID FK
string Abteilung
date Gueltig_von
date Gueltig_bis
}
%% ========================================
%% WEBSITES
%% ========================================
Websites {
int WebsiteID PK
string URL
int WebsiteTypID FK
}
WebsiteTypen {
int WebsiteTypID PK
string Typ
}
Kontakt_Website {
int Kontakt_WebsiteID PK
int KontaktID FK
int WebsiteID FK
}
Firma_Website {
int Firma_WebsiteID PK
int FirmaID FK
int WebsiteID FK
}
%% ========================================
%% BANKING
%% ========================================
Bankverbindungen {
int BankverbindungID PK
string IBAN
string BIC
int BankID FK
}
Banken {
int BankID PK
string Bankname
string BLZ
int LandID FK
}
Kontakt_Bankverbindung {
int Kontakt_BankverbindungID PK
int KontaktID FK
int BankverbindungID FK
bool IstHauptkonto
}
Firma_Bankverbindung {
int Firma_BankverbindungID PK
int FirmaID FK
int BankverbindungID FK
}
%% ========================================
%% STEUERN
%% ========================================
Steuer_IDs {
int SteuerID_TableID PK
string Nummer
int SteuerTypID FK
}
SteuerTypen {
int SteuerTypID PK
string Typ
int LandID FK
}
Kontakt_SteuerID {
int Kontakt_SteuerID_ID PK
int KontaktID FK
int SteuerID_TableID FK
}
Firma_SteuerID {
int Firma_SteuerID_ID PK
int FirmaID FK
int SteuerID_TableID FK
}
%% ========================================
%% BEZIEHUNGEN
%% ========================================
Beziehungen {
int BeziehungID PK
int KontaktID_1 FK
int KontaktID_2 FK
int BeziehungsTypID FK
date seit_Datum
}
BeziehungsTypen {
int BeziehungsTypID PK
string Typ
}
%% ========================================
%% SPRACHEN
%% ========================================
Kommunikationssprachen {
int KommunikationsspracheID PK
int KontaktID FK
int FirmaID FK
int SpracheID FK
int SprachlevelID FK
}
Sprachen {
int SpracheID PK
string Sprache
string ISO_Code
}
Sprachlevel {
int SprachlevelID PK
string Level
}
%% ========================================
%% TAGS
%% ========================================
Tags {
int TagID PK
string Tagname
int TagKategorieID FK
}
TagKategorien {
int TagKategorieID PK
string Kategoriename
}
Kontakt_Tag {
int Kontakt_TagID PK
int KontaktID FK
int TagID FK
}
Firma_Tag {
int Firma_TagID PK
int FirmaID FK
int TagID FK
}
%% ========================================
%% BEZIEHUNGEN (RELATIONSHIPS)
%% ========================================
%% Kontakte
Kontakte ||--o{ Anreden : "hat"
Kontakte ||--o{ Titel : "hat"
Titel ||--o{ TitelPositionen : "hat"
%% Firmen
Firmen ||--o{ Rechtsformen : "hat"
Firmen ||--o{ Branchen : "hat"
Kontakte ||--o{ Kontakt_Firma : "arbeitet bei"
Firmen ||--o{ Kontakt_Firma : "beschäftigt"
Kontakt_Firma ||--o{ Positionen : "in Position"
%% Adressen
Adressen ||--o{ Strassen : "liegt in"
Adressen ||--o{ PLZ : "hat"
Adressen ||--o{ Adresszusaetze : "hat"
Adressen ||--o{ AdressTypen : "vom Typ"
Strassen ||--o{ Orte : "in"
PLZ ||--o{ Orte : "gehört zu"
Orte ||--o{ Regionen : "in"
Regionen ||--o{ Laender : "in"
Laender ||--o{ Waehrungen : "hat"
Kontakte ||--o{ Kontakt_Adresse : "hat"
Adressen ||--o{ Kontakt_Adresse : "zugeordnet"
Firmen ||--o{ Firma_Adresse : "hat"
Adressen ||--o{ Firma_Adresse : "zugeordnet"
%% Telefon
Telefonnummern ||--o{ Laendervorwahlen : "hat"
Telefonnummern ||--o{ TelefonTypen : "vom Typ"
Laendervorwahlen ||--o{ Laender : "gehört zu"
Kontakte ||--o{ Kontakt_Telefon : "hat"
Telefonnummern ||--o{ Kontakt_Telefon : "zugeordnet"
Firmen ||--o{ Firma_Telefon : "hat"
Telefonnummern ||--o{ Firma_Telefon : "zugeordnet"
%% E-Mail
EMail_Adressen ||--o{ EmailTypen : "vom Typ"
Kontakte ||--o{ Kontakt_Email : "hat"
EMail_Adressen ||--o{ Kontakt_Email : "zugeordnet"
Firmen ||--o{ Firma_Email : "hat"
EMail_Adressen ||--o{ Firma_Email : "zugeordnet"
%% Websites
Websites ||--o{ WebsiteTypen : "vom Typ"
Kontakte ||--o{ Kontakt_Website : "hat"
Websites ||--o{ Kontakt_Website : "zugeordnet"
Firmen ||--o{ Firma_Website : "hat"
Websites ||--o{ Firma_Website : "zugeordnet"
%% Banking
Bankverbindungen ||--o{ Banken : "bei"
Banken ||--o{ Laender : "in"
Kontakte ||--o{ Kontakt_Bankverbindung : "hat"
Bankverbindungen ||--o{ Kontakt_Bankverbindung : "zugeordnet"
Firmen ||--o{ Firma_Bankverbindung : "hat"
Bankverbindungen ||--o{ Firma_Bankverbindung : "zugeordnet"
%% Steuern
Steuer_IDs ||--o{ SteuerTypen : "vom Typ"
SteuerTypen ||--o{ Laender : "in"
Kontakte ||--o{ Kontakt_SteuerID : "hat"
Steuer_IDs ||--o{ Kontakt_SteuerID : "zugeordnet"
Firmen ||--o{ Firma_SteuerID : "hat"
Steuer_IDs ||--o{ Firma_SteuerID : "zugeordnet"
%% Beziehungen zwischen Kontakten
Kontakte ||--o{ Beziehungen : "hat Beziehung zu"
Beziehungen ||--o{ BeziehungsTypen : "vom Typ"
%% Sprachen
Kommunikationssprachen ||--o{ Sprachen : "spricht"
Kommunikationssprachen ||--o{ Sprachlevel : "auf Level"
Kontakte ||--o{ Kommunikationssprachen : "spricht"
Firmen ||--o{ Kommunikationssprachen : "verwendet"
%% Tags
Tags ||--o{ TagKategorien : "in Kategorie"
Kontakte ||--o{ Kontakt_Tag : "hat"
Tags ||--o{ Kontakt_Tag : "zugeordnet"
Firmen ||--o{ Firma_Tag : "hat"
Tags ||--o{ Firma_Tag : "zugeordnet"
…