Natürliche Schlüssel und Denormalisierung
Kurz gesagt: Ein natürlicher Schlüssel stammt aus den Daten selbst, etwa eine E-Mail, ein Ersatzschlüssel ist eine Zahl ohne Bedeutung, und Denormalisierung ist eine Kopie von Daten, die du mit Absicht behältst.
Problem
Maria hat ihre E-Mail geändert
Wenn du die Kunden selbst entwirfst, wirkt die E-Mail wie ein guter Primärschlüssel. Sie ist eindeutig, jeder Kunde hat eine, und er meldet sich damit an. Hier ist so ein Entwurf: Die Kunden haben die E-Mail als Schlüssel, und die Bestellungen zeigen auf die E-Mail.
customers
- PK
emailtext nametext *citytext
orders
- PK
idinteger - FK
customer_emailtext *→ customers ordered_attext *
Maria ändert ihre E-Mail:
Die Datenbank lehnt die Änderung ab, weil drei Bestellungen auf die alte E-Mail zeigen. Wenn du auch die änderst, bleiben noch die schon verschickten Rechnungen, die Berichte und jedes andere System, das sich den alten Schlüssel gemerkt hat.
Die Idee
Natürliche und künstliche Schlüssel
Ein Schlüssel aus der Welt (eine E-Mail, eine Telefonnummer, die Nummer eines Buchs) ist ein natürlicher Schlüssel. Eine Zahl, die die Datenbank selbst vergibt und die außerhalb keine Bedeutung hat, ist ein Ersatzschlüssel (Surrogatschlüssel). Die id im Shop ist einer.
Ein Ersatzschlüssel ändert sich nie, weil ihn nichts in der Welt ändert. Die E-Mail ändert sich also in einer Zeile, und die Bestellungen merken nichts:
Die E-Mail behält ihre Regel. Sie ist kein Schlüssel mehr, aber UNIQUE, sodass zwei Kunden nicht dieselbe E-Mail haben können:
Und eine nationale Ausweisnummer (in Bulgarien die EGN)? Nein. Sie sind personenbezogene Daten, und der Schlüssel wird in jede Tabelle kopiert, die auf den Kunden zeigt. Ein Kunde aus dem Ausland hat meist keine. Und der Shop braucht sie nicht zu wissen.
Genauer, für Neugierige
ON UPDATE CASCADE in REFERENCES sorgt dafür, dass die Datenbank auch die Zeilen ändert, die auf den alten Schlüssel zeigen. Das hilft innerhalb der Datenbank, aber nicht bei den Kopien des Schlüssels außerhalb: in E-Mails, in Logs, in einem anderen System.
Ein natürlicher Schlüssel passt, wenn der Wert sich wirklich nie ändert und von außen bekannt ist. Beispiele sind ein Währungscode (EUR) oder ein Ländercode (BG).
Eine UUID ist eine andere Art Ersatzschlüssel: eine 128-Bit-Zahl, geschrieben als 550e8400-e29b-41d4-a716-446655440000. Sie lässt sich überall erzeugen, ohne die Datenbank zu fragen (in der App, auf einem Handy ohne Internet), zwei Datenbanken lassen sich ohne Konflikte zusammenführen, und die Adresse /orders/1043 verrät nicht mehr, wie viele Bestellungen der Shop hat. Der Preis ist, dass sie länger ist (16 Byte gegen 8 bei einer großen Ganzzahl) und zufällige UUIDs an zufälligen Stellen im Index landen und ihn verlangsamen. Deshalb gibt es auch zeitlich geordnete UUIDs (Version 7). PostgreSQL hat einen Typ uuid und eine Funktion gen_random_uuid(). SQLite hat keinen eigenen Typ und speichert sie als Text oder als BLOB.
Probier es aus
Eine neue E-Mail
Kunde 12, Boris, schreibt, dass seine neue E-Mail boris.m@example.com ist. Korrigiere sie.
Lösung
UPDATE customers SET email = 'boris.m@example.com' WHERE id = 12;UPDATE customers SET email = 'boris.m@example.com' WHERE id = 12. Eine Zeile. Seine drei Bestellungen zeigen auf id 12 und bleiben seine.
Die Idee
Der Preis in der Bestellzeile
order_items hat unit_price, und products hat price. Das sieht aus wie eine Wiederholung, die die Normalisierung entfernt. Erhöhe den Preis des Kaffees:
Die alten Bestellungen merken sich 14,90 €, während das Produkt 15,90 € kostet. Das sind nicht zwei Kopien einer Tatsache, sondern zwei verschiedene Tatsachen: „was es heute kostet“ und „was der Kunde bezahlt hat“. Würde die Bestellung ihren Preis aus products nehmen, würde jede Preiserhöhung auch vergangene Rechnungen ändern.
Die Idee
Denormalisierung
Die Summe der Bestellung ist anders. Sie wird wirklich aus den Positionen berechnet. Aber die Bestellliste auf der Website zeigt sie für jede Bestellung, und statt jedes Mal die Positionen zu addieren, kannst du sie in orders behalten:
Eine bewusste Kopie von Daten, die sich berechnen ließen, heißt Denormalisierung. Du machst sie, wenn Lesen viel häufiger ist als Ändern und die Berechnung teuer ist.
Sieh es dir an
Die Summe, die lügt
Ein Kollege fügt der Bestellung 60 Kaffee hinzu, vergisst aber total:
Die Website zeigt 14 €, während die Positionen 28,90 € ergeben. Nichts in der Datenbank hat das verhindert. Der Preis der Kopie ist: Jede Änderung an den Positionen muss auch die Summe ändern, im Programm, an jeder Stelle, die in order_items schreibt, oder mit einem Trigger (Modul „Transaktionen“).
Übung
Zwei Aufgaben
1. Die Buchhaltung vermutet, dass manche Summen falsch sind. Finde die Bestellungen, bei denen total nicht gleich der Summe der Positionen ist. Zeige die Nummer, die gespeicherte Summe und die echte.
Die Bestellungen mit falscher Summe:
Lösung
SELECT o.id, o.total, SUM(oi.quantity * oi.unit_price) AS items
FROM orders o
JOIN order_items oi ON oi.order_id = o.id
GROUP BY o.id, o.total
HAVING ROUND(o.total, 2) <> ROUND(SUM(oi.quantity * oi.unit_price), 2);Du gruppierst die Positionen nach Bestellung und vergleichst in HAVING. Zwei Bestellungen: 59 (13,90 € gespeichert, während die Positionen 8,90 € ergeben) und 60. ROUND(…, 2) verhindert, dass sich Dezimalzahlen in der letzten Stelle unterscheiden. Wenn du Kopien behältst, läuft eine solche Prüfabfrage regelmäßig.
2. Korrigiere die Summen der beiden Bestellungen, indem du sie aus den Positionen neu berechnest.
Aktualisiere total für die Bestellungen 59 und 60:
Lösung
UPDATE orders SET total = (
SELECT SUM(quantity * unit_price) FROM order_items
WHERE order_items.order_id = orders.id
)
WHERE id IN (59, 60);Dieselbe korrelierte Unterabfrage, die die Spalte gefüllt hat, aber nur für id IN (59, 60). Wenn du alle Bestellungen aktualisierst, ist das Ergebnis dasselbe, weil die anderen schon stimmen.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Fremdschlüssel: Verbindungen zwischen Tabellen · ALTER TABLE: eine Tabelle ändern · Beziehungstypen: 1:1, 1:N, M:N · Normalisierung
- Geht weiter in: OLTP und OLAP · Dokumentendatenbanken · Spaltenorientierte Datenbanken und Zeitreihen · Die Bäckerei, Etappe 1: Datenbank und Tabellen
- Aufgaben zum Thema: Eine Datenbank entwerfen