foreign keys vs primary keys

foreign keys vs primary keys

Stell dir vor, es ist Freitagabend, 22 Uhr. Dein Team hat gerade das neue E-Commerce-Modul ausgerollt. Plötzlich melden Kunden, dass ihre Bestellungen im Nirgendwo verschwinden oder – noch schlimmer – dass sie die Adressdaten völlig fremder Personen in ihrem Profil sehen. Ich habe dieses Szenario in den letzten fünfzehn Jahren bei drei verschiedenen Startups miterlebt. Der Grund war jedes Mal derselbe: Ein Entwickler dachte, er könne die Datenintegrität "einfach in der Applikationslogik abhandeln", anstatt die Datenbankregeln hart zu definieren. Die Reparatur solcher inkonsistenten Datensätze dauert Wochen, kostet Zehntausende Euro an Arbeitszeit und zerstört das Vertrauen der Nutzer nachhaltig. Wer die Debatte Foreign Keys vs Primary Keys nur als theoretisches Informatik-Konzept abtut, hat noch nie versucht, eine Datenbank mit zwei Millionen verwaisten Einträgen manuell zu bereinigen.

Der Trugschluss der künstlichen Eindeutigkeit

Ein Fehler, der mir ständig begegnet, ist die paranoide Angst vor natürlichen Schlüsseln. Viele Architekten pressen jedes Objekt in eine Tabelle mit einer automatisch hochzählenden ID als Hauptidentifikationsmerkmal. Das ist an sich okay, wird aber zum Problem, wenn man vergisst, was die Daten im Kern eigentlich unterscheidbar macht.

Ich sah einmal ein System für eine Versicherung, bei dem jeder Versicherungsnehmer eine fortlaufende Nummer erhielt. Klingt logisch. Aber es gab keine Unique-Constraints auf die Kombination aus Name, Geburtsdatum und Postleitzahl. Das Resultat? Wir hatten nach zwei Jahren Betrieb über 4.000 Dubletten. Kunden hatten sich mehrfach registriert, Berater hatten Daten falsch eingegeben. Da der Hauptschlüssel technisch immer "einzigartig" war (die ID 101 ist eben ungleich 102), hat die Datenbank nicht gemeckert. Die Realität war jedoch ein Datenfriedhof.

Die Lösung ist simpel, wird aber oft ignoriert: Nur weil du eine technische ID verwendest, darfst du die fachliche Eindeutigkeit nicht vernachlässigen. Du musst zusätzlich Unique-Indizes auf die Felder legen, die einen Datensatz in der echten Welt identifizieren. Wenn du das nicht tust, ist dein Hauptschlüssel nur eine bedeutungslose Zahl, die Chaos verschleiert.

Foreign Keys vs Primary Keys als Fundament der Konsistenz

Hier trennt sich die Spreu vom Weizen. In der Theorie weiß jeder, dass eine Verknüpfung zwischen Tabellen existieren sollte. In der Praxis schalten viele Teams die harten Verknüpfungen ab, weil sie Angst vor Performance-Einbußen bei Massen-Imports haben oder weil ihr Framework sie dazu verleitet.

Ein klassisches Beispiel aus meiner Zeit bei einem Logistikdienstleister: Die Entwickler löschten Datensätze in der Tabelle "Fahrzeuge", vergaßen aber, die zugehörigen Wartungsprotokolle in der Tabelle "Wartung" zu entfernen. Da keine harten Verbindungen auf Datenbankebene existierten, blieben die Wartungsdaten als Datenleichen zurück. Monate später versuchten Analyse-Skripte, Berichte über die Flotte zu erstellen und stürzten ab, weil sie Informationen zu Autos suchten, die offiziell gar nicht mehr existierten.

Ohne eine strikte Durchsetzung dieser Beziehungen auf Ebene der Datenbank-Engine baust du dir eine Zeitbombe. Ein harter Verweis erzwingt, dass ein Wert in der Zieltabelle auch wirklich als Identifikator vorhanden ist. Wenn du versuchst, einen Kunden zu löschen, der noch offene Rechnungen hat, muss die Datenbank "Nein" sagen. Wer das im Code der Applikation regeln will, verliert garantiert. Ein Bug im Code, ein vergessener if-Check oder ein manuelles SQL-Statement eines Administrators reicht aus, um die Integrität zu sprengen.

📖 Verwandt: im not a robot

Das Märchen von der Performance-Bremse

Es hält sich hartnäckig das Gerücht, dass harte Verweise die Datenbank langsam machen. "Wir brauchen maximale Geschwindigkeit, deshalb lassen wir die Constraints weg", heißt es dann oft. Das ist meistens völliger Unsinn. Ja, die Datenbank muss bei jedem Insert oder Update prüfen, ob die Beziehung gültig ist. Aber dieser Overhead ist im Vergleich zu den Kosten für kaputte Daten vernachlässigbar.

Viel öfter ist das Problem, dass die Leute vergessen, Indizes auf die Verweisfelder zu legen. Wenn du eine Tabelle mit Bestellungen hast und jedes Mal prüfen musst, ob die Kunden_ID existiert, die Tabelle "Kunden" aber keinen Index auf dieser ID hat, dann wird es langsam. Das liegt aber nicht an der Verknüpfung an sich, sondern an schlechtem Index-Design.

In einem Projekt für ein Buchungssystem haben wir die harten Constraints nachträglich eingeführt. Die Schreibvorgänge wurden messbar langsamer – wir sprachen von etwa 3 bis 5 Millisekunden pro Transaktion. Aber im Gegenzug sparten wir uns die nächtlichen Validierungs-Skripte, die vorher drei Stunden liefen, nur um Datenfehler zu finden und zu protokollieren. Am Ende war das System insgesamt stabiler und effizienter.

Die Falle der kaskadierenden Löschungen

Das Feature ON DELETE CASCADE wird oft wie ein Zauberstab benutzt. Man löscht den Hauptdatensatz, und die Datenbank räumt netterweise alles andere drumherum mit auf. Das ist bequem, führt aber in großen Systemen zu Katastrophen.

Ich erinnere mich an einen Fall, bei dem ein Junior-Admin versehentlich eine Test-Kategorie in einem Content-Management-System löschte. Da die Verknüpfungen so eingestellt waren, dass alles gelöscht wurde, was an dieser Kategorie hing, verschwanden innerhalb von Sekunden 50.000 Artikel, Kommentare und Bilder. Die Datenbank tat genau das, was ihr befohlen wurde.

💡 Das könnte Sie interessieren: olympus om de m10

Die bessere Strategie: Verwende RESTRICT oder NO ACTION. Zwinge dich oder deine Entwickler dazu, die abhängigen Daten explizit zu behandeln. Wenn du etwas löschen willst, das noch Abhängigkeiten hat, soll die Datenbank einen Fehler werfen. Das ist kein Bug, das ist eine Sicherheitsfunktion. Es zwingt dich, darüber nachzudenken, was mit den verwaisten Daten passieren soll. Müssen sie archiviert werden? Sollen sie anonymisiert werden? Ein automatisches Löschen nimmt dir diese Entscheidung ab – oft mit fatalen Folgen.

UUIDs als moderne Stolperfalle

In verteilten Systemen greifen heute fast alle zu UUIDs (Universally Unique Identifiers) statt zu einfachen Integers. Das hat Vorteile, bringt aber bei der Abwägung Foreign Keys vs Primary Keys neue Probleme mit sich. Eine UUID ist groß, unhandlich und – was viele unterschätzen – oft nicht sortiert.

Wenn du eine UUID als Hauptschlüssel verwendest und diese zufällig generiert wird, zerlegst du die physische Sortierung deiner Daten auf der Festplatte (den sogenannten Clustered Index). Jedes Mal, wenn ein neuer Datensatz eingefügt wird, muss die Datenbank ihn irgendwo in die Mitte der bestehenden Daten quetschen, anstatt ihn einfach hinten anzuhängen. Das führt zu massiver Fragmentierung.

In einem Projekt mit hohen Schreiblasten sahen wir, wie die Festplatten-IOPS durch die Decke gingen, nur weil wir von BIGINT auf zufällige UUIDs umstellten. Die Lösung hier ist der Einsatz von zeitlich sortierbaren UUIDs (wie UUIDv7 oder ULIDs). Du hast damit die Vorteile der globalen Eindeutigkeit, behältst aber die Performance beim Einfügen. Wer hier einfach zum Standard-Generator greift, zahlt später bei den Cloud-Kosten für schnellere Festplatten drauf.

Vorher und Nachher: Ein praktischer Vergleich

Schauen wir uns an, wie sich ein System ohne saubere Struktur im Vergleich zu einem professionell aufgesetzten System verhält.

🔗 Weiterlesen: diesen Leitfaden

Szenario ohne strikte Regeln: Ein Entwickler erstellt eine Tabelle für "Produkte" und eine für "Lagerbestand". In "Lagerbestand" steht einfach eine Zahl, die das Produkt identifizieren soll. Es gibt keine harten Constraints. Ein Produkt wird aus dem Sortiment genommen und gelöscht. Das Skript vergisst jedoch, den Lagerbestand zu nullen oder zu löschen. Wochen später wundert sich die Buchhaltung, warum laut System Waren im Wert von 200.000 Euro im Lager liegen, für die es keine Produktbeschreibung mehr gibt. Der Bericht ist wertlos. Ein Mitarbeiter muss nun drei Tage lang manuell Abgleiche in Excel fahren, um herauszufinden, was diese IDs mal bedeuteten.

Szenario mit strikten Regeln: Dieselben Tabellen, aber diesmal mit einem Foreign Key auf der Produkt-ID im Lagerbestand. Der Entwickler versucht, das Produkt zu löschen. Die Datenbank meldet sofort: "Fehler: Datensatz kann nicht gelöscht werden, da noch Bestände in der Tabelle Lagerbestand existieren." Der Entwickler wird gestoppt. Er erkennt, dass er erst den Bestand ausbuchen oder umlagern muss. Die Daten bleiben konsistent. Die Buchhaltung bekommt auf Knopfdruck korrekte Zahlen. Die Zeitersparnis ist massiv, da die Fehlervermeidung automatisiert ist.

Was es wirklich braucht: Ein Realitätscheck

Datenbankdesign ist kein Thema, das man einmal abhakt und dann vergisst. Es ist harte, oft undankbare Arbeit, die Disziplin erfordert. Wenn du glaubst, du könntest Abkürzungen nehmen, indem du Constraints weglässt, um schneller zu entwickeln, dann irrst du dich gewaltig. Du kaufst dir diese Geschwindigkeit mit technischen Schulden, die später mit extrem hohen Zinsen zurückgezahlt werden müssen.

In der echten Welt gibt es keine perfekten Schemata, die für die Ewigkeit halten. Anforderungen ändern sich. Aber die Grundlagen der relationalen Integrität sind seit Jahrzehnten stabil aus gutem Grund. Wer sie ignoriert, handelt grob fahrlässig.

Es erfordert Mut, einem Projektleiter zu sagen, dass ein Feature zwei Tage länger dauert, weil man die Migrationen für die Constraints und die Bereinigung der Altdaten ordentlich machen muss. Aber diese zwei Tage sind nichts gegen den Monat, den du verlierst, wenn die Datenbank korrupt ist.

  • Verlass dich niemals auf das Frontend oder das Backend-Framework, um die Integrität zu wahren.
  • Nutze Indizes für jeden Verweis, sonst killst du deine Performance.
  • Sei vorsichtig mit Automatismen wie dem kaskadierenden Löschen.
  • Teste dein Schema mit echten Datenmengen, bevor du live gehst.

Am Ende des Tages ist die Datenbank die "Single Source of Truth". Wenn diese Wahrheit durch fehlende Regeln verwässert wird, ist dein gesamtes Softwaresystem nichts weiter als ein Kartenhaus, das beim nächsten Windstoß zusammenbricht. Es gibt keine magischen Tools, die dir diese Arbeit abnehmen. Du musst dein Handwerk verstehen und die Regeln konsequent anwenden, auch wenn es unbequem ist. Nur so sparst du dir den nächtlichen Anruf und den Frust deiner Kunden. Wer hier spart, spart am falschen Ende und wird es früher oder später bereuen. Ist nun mal so. Da hilft auch kein Schönreden. Wer Erfolg haben will, muss die Langeweile sauberer Datenstrukturen akzeptieren. Klappt sonst einfach nicht auf Dauer.

MS

Martin Schulz

Martin Schulz hat für verschiedene Online-Redaktionen gearbeitet und steht für Qualitätsjournalismus mit Substanz.