Premium
Grundlagen
5 Min. Lesezeit·

Normalisierung von Datenbanken: 1NF, 2NF und 3NF an einem Beispiel

Normalisierung bedeutet, die Struktur einer Datenbank so umzubauen, dass jeder Fakt genau einmal gespeichert wird. Die Regeln der Normalisierung sind als Stufen beschrieben — Normalformen: Jede weitere Form beseitigt ihre eigene Art von Duplizierung. In der Praxis genügen in den allermeisten Fällen die ersten drei.

Gehen wir alle drei Formen an einem durchgängigen Beispiel durch, das den Weg von der „Alles-in-einem-Tabelle" zum sauberen Schema zurücklegt.

Der Ausgangspunkt: alles in einer Tabelle

Ein Essenslieferdienst speichert seine Bestellungen so:

order_idcustomerphoneitemsprices
1Anna+49 151 000-11-22Pizza, Cola12.99, 2.49
2Boris+49 160 555-66-77Sushi-Set24.99
3Anna+49 151 000-11-22Cola, Chips2.49, 2.99

Sieht kompakt aus, ist aber unbrauchbar:

  • Cola-Verkäufe lassen sich nicht zählen — die Produkte sind zu einem String verklebt, es bleibt nur LIKE '%Cola%' und die Hoffnung, dass nie eine „Cola Zero" auftaucht.
  • Anna hat eine neue Telefonnummer — aktualisiert werden müssen alle ihre Bestellungen. Eine Zeile übersehen, und die Datenbank enthält zwei verschiedene Nummern, ohne dass sich klären lässt, welche stimmt.
  • Bestellung 2 wurde gelöscht — und mit ihr verschwand Boris samt Telefonnummer aus der Datenbank.
  • Ein neuer Kunde ohne Bestellung — es gibt schlicht keinen Ort, ihn zu erfassen.

Diese Situationen heißen Änderungs-, Einfüge- und Löschanomalien. Genau dafür gibt es die Normalisierung: damit sie unmöglich werden.

1NF: ein Wert pro Zelle

Die erste Normalform verlangt Atomarität: Am Schnittpunkt von Zeile und Spalte steht ein unteilbarer Wert — keine Listen, keine Aufzählungen. Wir falten jede Bestellposition in eine eigene Zeile aus:

order_idcustomerphoneproductprice
1Anna+49 151 000-11-22Pizza12.99
1Anna+49 151 000-11-22Cola2.49
2Boris+49 160 555-66-77Sushi-Set24.99
3Anna+49 151 000-11-22Cola2.49
3Anna+49 151 000-11-22Chips2.99

Cola-Verkäufe sind jetzt ein gewöhnliches GROUP BY. Eine Zeile wird eindeutig durch das Paar (order_id, product) bestimmt — den zusammengesetzten Schlüssel der Tabelle.

Doch die Duplizierung ist sogar gewachsen: Annas Name und Telefonnummer wiederholen sich in jeder Zeile jeder ihrer Bestellungen. Weiter geht's.

Eine Mitarbeitertabelle hat eine Spalte skills mit Werten wie 'SQL, Python, Excel'. Welche Normalisierungsregel ist verletzt?

2NF: ein Fakt hängt vom ganzen Schlüssel ab, nicht von einem Teil

Die zweite Normalform betrachtet Tabellen mit zusammengesetztem Schlüssel und verlangt: Jede Nicht-Schlüssel-Spalte muss vom gesamten Schlüssel (order_id, product) abhängen, nicht von einem Teil davon. Wir haben zwei Verstöße:

  • customer und phone hängen nur von order_id ab — wer bestellt hat, ändert sich nicht mit dem Produkt in der Zeile;
  • price hängt nur von product ab — der Preis der Pizza ist in allen Bestellungen derselbe.

Wir verlagern jeden Fakt in eine Tabelle, in der er von seinem Schlüssel vollständig abhängt:

MySQL 8.1
CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer VARCHAR(100),
    phone VARCHAR(20)
);

CREATE TABLE products (
    product_id INT PRIMARY KEY,
    name VARCHAR(100),
    price DECIMAL(10,2)
);

CREATE TABLE order_items (
    order_id INT REFERENCES orders(order_id),
    product_id INT REFERENCES products(product_id),
    quantity INT,
    PRIMARY KEY (order_id, product_id)
);

Der Produktpreis steht jetzt einmal in products, der Inhalt einer Bestellung sind kompakte Identifikator-Paare in order_items.

3NF: Nicht-Schlüssel-Spalten hängen nicht voneinander ab

Die dritte Normalform erledigt die letzte Duplizierung — transitive Abhängigkeiten, bei denen eine Nicht-Schlüssel-Spalte nicht vom Schlüssel, sondern von einer anderen Nicht-Schlüssel-Spalte abhängt. In der Tabelle orders ist genau das übrig: phone hängt von customer ab, nicht von der Bestellnummer. Annas Telefonnummer wiederholt sich immer noch in jeder ihrer Bestellungen.

Wir geben dem Kunden eine eigene Tabelle:

MySQL 8.1
CREATE TABLE customers (
    customer_id INT PRIMARY KEY,
    name VARCHAR(100),
    phone VARCHAR(20)
);

CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT REFERENCES customers(customer_id),
    order_date TIMESTAMP
);

Die klassische Formel bringt die 3NF auf den Punkt: Jede Nicht-Schlüssel-Spalte hängt ab „vom Schlüssel, vom ganzen Schlüssel und von nichts außer dem Schlüssel".

Eine Bestelltabelle enthält die Spalten city und city_zip, wobei die Postleitzahl eindeutig durch die Stadt bestimmt ist. Welche Form ist verletzt?

Das fertige Schema

Aus einer Alles-in-einem-Tabelle wurden vier verbundene Tabellen — das Schema lässt sich live erkunden:

Prüfen wir, was aus den Anomalien wurde: Annas Telefonnummer ändert sich an einer Stelle; das Löschen einer Bestellung rührt den Kunden nicht an; ein Kunde ohne Bestellung lebt ruhig in customers; Cola-Verkäufe werden über order_items gezählt — ganz ohne LIKE.

Der Weg der Normalisierung: von der Alles-in-einem-Tabelle zu drei Normalformen

Wann Duplizierung gerechtfertigt ist: Denormalisierung

Normalisierung ist kein Selbstzweck, sondern ein Schutz der Daten vor dem Auseinanderlaufen. Manchmal weicht man bewusst von ihr ab:

  • Momentaufnahme. Der Preis in products ändert sich, aber auf dem Beleg einer bezahlten Bestellung muss er so bleiben, wie er beim Kauf war. Deshalb bekommt order_items oft eine eigene price-Spalte — kein Fehler, sondern ein festgehaltener historischer Fakt.
  • Vorberechnete Summen. Die Bestellsumme lässt sich jedes Mal aus den Positionen berechnen, aber bei Tausenden Anfragen pro Sekunde ist es günstiger, total_amount in orders zu speichern und bei Änderungen zu aktualisieren.
  • Analytik. In Reporting-Schichten werden Daten absichtlich wieder in breite Tabellen „zurückgefaltet", damit Abfragen nicht ein Dutzend Joins zusammensetzen müssen.

Das Schlüsselwort ist bewusst: Denormalisierung bedeutet immer, dass für die Übereinstimmung der Duplikate jetzt Ihr Code verantwortlich ist, nicht das DBMS.

Wie weiter

  • woraus eine solide Tabellenstruktur besteht — in der Lektion Struktur relationaler Datenbanken;
  • die Primär- und Fremdschlüssel, auf denen die Beziehungen ruhen — in der Lektion Constraints;
  • dem Entwurf von Schemata „vom Geschäftsproblem aus", inklusive Normalisierung, Denormalisierung und Datenhistorie, widmet sich ein ganzer Kurs — Datenbankdesign und Architektur.

Passende Artikel