Wenn wir Daten perfekt normalisieren, wird jede Tabelle so kompakt wie möglich und die Infos darin folgen strikt einem Prinzip. Aber um echte Abfragen zu machen (zum Beispiel: "Welche Studis sind im SQL-Kurs eingeschrieben?"), muss man oft zig Tabellen zusammenjoinen. Je mehr Tabellen, desto komplizierter die Queries, desto mehr muss das System "schaufeln".
Du kennst bestimmt schon JOINs aus den vorherigen Vorlesungen. Hier ein Beispiel für eine Query, die man bei einer sauber designten Datenbank braucht:
SELECT students.name, courses.title
FROM students
JOIN enrollments ON students.id = enrollments.student_id
JOIN courses ON enrollments.course_id = courses.id
WHERE courses.title = 'SQL';
Klingt easy, aber unter der Haube macht der Server richtig Arbeit: jede Tabelle lesen, Daten verbinden, filtern... Und was, wenn die Tabellen riesig sind? Logisch, dass die Performance dann runtergeht.
Battle: Normalisierung vs. Speed
Zum Glück (oder leider?) sind Datenbanken im echten Leben immer ein Kompromiss. Volle Normalisierung sorgt für Daten-Integrität, aber macht komplexe Queries langsam. Wenn die Datenbank für Analytics und Reports genutzt wird, lohnt sich manchmal Denormalisierung. Das ist wie 10 kleine Boxen durch eine große Truhe ersetzen: Daten rausholen geht schneller, aber wieder sortieren wird schwieriger.
Wann kann man bei der Normalisierung mal locker lassen?
Es gibt Szenarien, wo Denormalisierung besser ist:
Oft genutzte Aggregates
Stell dir vor, das System fragt jeden Tag ab, wie viele Studis in jedem Kurs sind. In einer normalisierten Struktur müsste man ständigJOIN und
COUNT() machen. Stattdessen kann man in der Tabelle "Courses" eine Spalte
student_count anlegen, die automatisch beim Hinzufügen/Löschen von Einträgen aktualisiert wird.
-- Denormalisierte Spalte
UPDATE courses
SET student_count = (
SELECT COUNT(*)
FROM enrollments
WHERE enrollments.course_id = courses.id
);
Oft genutzte Reports
Wenn dein Kunde jeden Tag einen Report will wie "Wer hat was wann gekauft?", ist es einfacher, eine denormalisierte Tabelle mit fertigen Zeilen wie "Kundenname, Produkt, Datum" zu speichern. Die Haupttabelle wird größer, aber die Daten kommen schneller raus.
Viel Lesen, wenig Schreiben
Wenn die Datenbank hauptsächlich für Reads (z.B. Analytics) genutzt wird, kann man für Speed auf Normalisierung verzichten.
Weniger Joins bei komplexen Beziehungen
Wenn zwischen Tabellen verschachtelte (nested) Beziehungen sind undJOIN zum Albtraum wird, kann man ein paar Normalisierungsstufen rausnehmen.
Beispiel: Wie macht Denormalisierung alles schneller?
Wir haben normalisierte Tabellen für einen Online-Shop:
Tabelle products |
Tabelle orders |
Tabelle order_items |
|---|---|---|
| id | id | id |
| name | date | order_id |
| price | customer_id | product_id |
| quantity |
Jede Bestellung (orders) besteht aus Bestellzeilen (order_items). Lass uns berechnen, wie viel Kohle der Shop gemacht hat:
SELECT SUM(order_items.quantity * products.price) AS total_revenue
FROM order_items
JOIN products ON order_items.product_id = products.id;
Das Joinen von order_items und products macht die Query bei großen Datenmengen langsam.
Denormalisierte Struktur
Jetzt stell dir vor, in der Tabelle order_items gibt's eine "extra" Spalte total_price (Denormalisierung):
Tabelle order_items |
|---|
| id |
| order_id |
| product_id |
| quantity |
| total_price |
Jetzt ist die Query super simpel:
SELECT SUM(total_price) AS total_revenue
FROM order_items;
So sparen wir uns das JOIN und machen alles schneller.
Praxisaufgabe: Optimierung der "Verkäufe"-Datenbank
Gegeben: normalisierte Tabellen
Tabelle products |
Tabelle sales |
|---|---|
| id | id |
| name | product_id |
| price | date |
| quantity |
Aufgabe: Mach häufige Queries wie "Wie viel wurde mit jedem Produkt verdient?" schneller.
Schritt 1: Füge die Spalte total_price zur Tabelle sales hinzu:
ALTER TABLE sales ADD COLUMN total_price NUMERIC;
Schritt 2: Fülle die Spalte mit den bestehenden Daten:
UPDATE sales
SET total_price = quantity * (
SELECT price
FROM products
WHERE products.id = sales.product_id
);
Schritt 3: Jetzt gehen die Queries schneller:
SELECT product_id, SUM(total_price) AS total_revenue
FROM sales
GROUP BY product_id;
Aber! Denormalisierung hat ihren Preis
Du weißt ja, "schneller" ist nicht immer "besser". Bei Denormalisierung gibt's Probleme:
Redundanter Speicher
Die Spaltetotal_price ist eine Kopie von Daten und braucht extra Platz.
Komplizierte Updates
Wenn sich der Preis eines Produkts in der Tabelle products ändert, musst du die entsprechende Spalte total_price manuell updaten. Das kann zu Inkonsistenzen führen.
Anomalien bei Insert, Update und Delete
Die Infos können schnell "aus dem Takt" kommen, wenn man vergisst, die denormalisierten Daten zu aktualisieren. Zum Beispiel, wenn sich der Produktpreis ändert, passiert das nicht automatisch.
Balance: Wie findet man das goldene Mittel?
Entscheide, was wichtiger ist: Speed oder Struktur? Wenn die DB viel gelesen wird, richte dich nach den Queries.
Denormalisiere nur gezielt. Zum Beispiel nur für wichtige Zahlen und Reports.
Automatisiere das Updaten der denormalisierten Daten. Nutze Trigger oder Tasks, damit keine Inkonsistenzen entstehen.
GO TO FULL VERSION