Kodokon kodokon.com

Datenmodellierung: Normalisierung und Denormalisierung

Strukturiere deine Schemata von der 1NF bis zur 3NF, um Anomalien zu beseitigen, und wisse, wann du bewusst denormalisierst.

10 Min. · 3 Fragen

Diese Lektion in Kodokon öffnen

Ein schlechtes Schema kostet dich über Jahre. Die Normalisierung verfolgt ein einziges Ziel: dass jede Tatsache nur einmal gespeichert wird, um drei Anomalien zu beseitigen: die Änderungsanomalie (eine E-Mail an zehn Stellen ändern), die Einfügeanomalie (kein Produkt ohne eine Bestellung hinzufügen zu können) und die Löschanomalie (das Löschen der letzten Bestellung löscht die Kundin oder den Kunden). Sieh dir dieses absichtlich verpfuschte Schema an.

SQL
CREATE TABLE bad_orders (
  id INTEGER PRIMARY KEY,
  customer_name TEXT,
  customer_email TEXT,
  product_names TEXT,
  total REAL
);

INSERT INTO bad_orders
  (customer_name, customer_email,
   product_names, total)
VALUES
  ('Alice', 'alice@mail.com',
   'keyboard,mouse', 74.0),
  ('Alice', 'alice@mail.co',
   'monitor', 199.0);
Eine Liste in einer Spalte, eine mit Tippfehler duplizierte E-Mail: alles ist da.

Die ersten drei Normalformen in der Praxis. 1NF: atomare Werte - product_names = 'keyboard,mouse' verletzt sie und macht jedes WHERE oder JOIN auf Produkte unpraktikabel. 2NF (zusammengesetzter Schlüssel): Jede Spalte hängt vom gesamten Schlüssel ab - den Katalogpreis eines Produkts in einer durch (Bestellung, Produkt) identifizierten Bestellzeile zu speichern verletzt sie, weil der Preis nur vom Produkt abhängt. 3NF: Keine Nichtschlüsselspalte hängt von einer anderen Nichtschlüsselspalte ab - customer_email, das von customer_name abhängt, verletzt sie; daher das Duplikat alice@mail.co. Hier ist das korrigierte Schema.

SQL
CREATE TABLE customers (
  id INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  email TEXT NOT NULL UNIQUE
);

CREATE TABLE products (
  id INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  price REAL NOT NULL
);

CREATE TABLE purchases (
  id INTEGER PRIMARY KEY,
  customer_id INTEGER NOT NULL
    REFERENCES customers(id)
);

CREATE TABLE purchase_items (
  purchase_id INTEGER NOT NULL
    REFERENCES purchases(id),
  product_id INTEGER NOT NULL
    REFERENCES products(id),
  quantity INTEGER NOT NULL,
  PRIMARY KEY (purchase_id, product_id)
);
Jede Tatsache lebt an einer einzigen Stelle; Fremdschlüssel verbinden alles miteinander.
SQL
PRAGMA foreign_keys = ON;

INSERT INTO customers (name, email)
VALUES ('Alice', 'alice@mail.com');

INSERT INTO products (name, price)
VALUES ('keyboard', 49.0), ('mouse', 25.0);

INSERT INTO purchases (customer_id) VALUES (1);

INSERT INTO purchase_items
  (purchase_id, product_id, quantity)
VALUES (1, 1, 1), (1, 2, 1);

SELECT c.name, p.name AS product,
       i.quantity, p.price
FROM purchase_items AS i
JOIN purchases AS pu ON pu.id = i.purchase_id
JOIN customers AS c ON c.id = pu.customer_id
JOIN products AS p ON p.id = i.product_id;
Das vollständige Detail wird über Joins rekonstruiert.

Wann solltest du also denormalisieren? Wenn die Lesevorgänge zum gemessenen Engpass werden: Joins über fünf Tabellen auf einem kritischen Bildschirm, ein bei jedem Rendern neu berechnetes Aggregat. Die ehrlichen Muster: eine Cache-Spalte (purchases.total, zum Bestellzeitpunkt eingefroren - und das zu Recht: Der gezahlte Preis ist eine historische Tatsache, verschieden vom Katalogpreis), ein per Trigger gepflegter Zähler oder eine im Batch neu aufgebaute Reporting-Tabelle. Der Preis dafür: Jede Kopie kann abweichen, und nun garantiert dein Code die Konsistenz, nicht mehr die Engine.

Wissenscheck

Stelle sicher, dass du die wichtigsten Punkte dieser Lektion behalten hast.

  1. Gegen welche Normalform verstößt die Spalte product_names, die 'keyboard,mouse' enthält?
    • 1NF
    • 2NF
    • 3NF
  2. Welches Symptom deutet auf eine 3NF-Verletzung hin?
    • Ein Primärschlüssel aus zwei Spalten
    • Eine Nichtschlüsselspalte, die von einer anderen Nichtschlüsselspalte abhängt
    • Eine Tabelle ohne Sekundärindex
  3. Wann ist Denormalisierung gerechtfertigt?
    • Schon in der Entwurfsphase, um künftige Joins zu sparen
    • Nach der Messung, bei kritischen Lesevorgängen, unter Inkaufnahme des Risikos von Inkonsistenzen
    • Niemals: Ein Schema muss um jeden Preis in 3NF bleiben