CodeGym /Kurse /SQL SELF /Balance zwischen Normalisierung und Performance

Balance zwischen Normalisierung und Performance

SQL SELF
Level 26 , Lektion 3
Verfügbar

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ändig JOIN 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 und JOIN 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 Spalte total_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.

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION