SQL-Datenbanken sind die „Arbeitspferde“ für strukturierte Daten: Bestellungen, Benutzer, Buchhaltung, Lager, Logs mit Struktur, Rechteverwaltung und vieles mehr. Sie sind besonders stark, wenn Daten Beziehungen haben und du verlässliche Konsistenz brauchst.
In relationalen Datenbanken werden Daten in Tabellen gespeichert. Eine Tabelle hat Spalten (Attribute) und Zeilen (Datensätze). Jede Spalte hat einen Datentyp (z. B. Zahl, Text, Datum).
Stell dir eine Excel-Tabelle vor – nur robust, parallel nutzbar, mit Regeln (Constraints) und für sehr große Datenmengen optimiert.
Relationale Systeme erzwingen Schema-Constraints (je nach DB mehr/ weniger strikt) und unterstützen deklarative Abfragen, Optimierung über Query-Planner, Index-Strukturen und Transaktions-Isolation.
CREATE TABLE users ( id BIGINT PRIMARY KEY, email TEXT NOT NULL UNIQUE, created_at TIMESTAMP NOT NULL );
Der wichtigste Unterschied zu „nur Tabellen“ ist: SQL-Datenbanken können Beziehungen sauber abbilden. Dafür nutzt man Primärschlüssel (eindeutige Identifikation) und Fremdschlüssel (Verweise).
| Beziehung | Beispiel | Wie abbilden? | Wichtigster Punkt |
|---|---|---|---|
| 1 : 1 | user ↔ user_profile | Profil-Tabelle mit UNIQUE-FK | Trennung sensibler / optionaler Daten |
| 1 : n | user → orders | orders.user_id als FK | Sehr häufig im Business |
| n : m | users ↔ roles | Zwischentabelle (user_roles) | Join-Table ist Standard |
CREATE TABLE orders ( id BIGINT PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users(id), total_cents INT NOT NULL, created_at TIMESTAMP NOT NULL );
Normalisierung bedeutet: Daten so strukturieren, dass du Wiederholungen vermeidest und Änderungen sauber bleiben. Ziel ist nicht „akademisch perfekt“, sondern: weniger Fehler und weniger widersprüchliche Daten.
Adressen oder Produktinfos werden mehrfach kopiert (z. B. in jeder Bestellung). Später ändert sich etwas – und du hast 20 unterschiedliche Stände.
Häufige, wiederverwendete Dinge bekommen eigene Tabellen (z. B. products, customers). „Beziehungsdaten“ kommen in Join-Tabellen.
JOINs verbinden Tabellen. Damit kannst du z. B. „alle Bestellungen mit User-E-Mail“ abfragen, ohne Daten doppelt zu speichern.
| JOIN | Einfach erklärt | Wann nutzen? | Typischer Stolperstein |
|---|---|---|---|
| INNER JOIN | Nur Treffer in beiden Tabellen | Wenn Beziehung zwingend ist | Fehlende Zeilen verschwinden |
| LEFT JOIN | Alles links, rechts optional | Wenn du auch „ohne Beziehung“ sehen willst | WHERE auf rechter Tabelle kann LEFT JOIN „kaputt machen“ |
SELECT o.id, o.total_cents, u.email FROM orders o JOIN users u ON u.id = o.user_id WHERE o.total_cents >= 5000 ORDER BY o.created_at DESC;
Indexe sind Datenstrukturen, die das Finden von Zeilen beschleunigen. Ohne Index kann eine DB viele Zeilen „durchscannen“. Mit Index kann sie schneller springen (z. B. B-Tree bei vielen Systemen).
CREATE INDEX idx_orders_user_created ON orders (user_id, created_at DESC);
Transaktionen bündeln mehrere SQL-Operationen zu einer Einheit. Entweder alles wird gespeichert – oder nichts. Das ist essenziell für Geld, Bestände, Buchungen, Statuswechsel.
| ACID | Bedeutung | Einfach erklärt |
|---|---|---|
| Atomicity | Alles oder nichts | Keine halbfertigen Änderungen |
| Consistency | Korrekte Zustände | Constraints bleiben gültig |
| Isolation | Abschirmung | Parallele Transaktionen stören sich nicht unkontrolliert |
| Durability | Dauerhaftigkeit | Nach COMMIT bleibt es auch nach Crash erhalten |
BEGIN; UPDATE accounts SET balance_cents = balance_cents - 2500 WHERE id = 10; UPDATE accounts SET balance_cents = balance_cents + 2500 WHERE id = 20; COMMIT;
Wenn 100 Nutzer gleichzeitig bestellen, greifen mehrere Transaktionen parallel auf gleiche Daten zu. Die DB sorgt über Isolation/Locks dafür, dass es nicht zu kaputten Zuständen kommt.
SQL ist deklarativ – du sagst was du willst. Der Query-Planner entscheidet, wie er es findet: welche Indexe, welche Join-Reihenfolge, welche Scans.
Mit EXPLAIN (oder DB-spezifisch EXPLAIN ANALYZE) siehst du den Plan: Scans, Kosten, geschätzte Zeilen, tatsächliche Laufzeiten.
-- Statt: OFFSET 500000 (wird langsam) SELECT id, created_at FROM orders WHERE id < :last_id ORDER BY id DESC LIMIT 50;
Sicherheit ist nicht optional. Der häufigste Fehler ist SQL Injection durch String-Konkatenation. Dazu kommen Rechteverwaltung, Backups, Zugriffsschutz und Audit-Logs.
Eine Datenbank ist nicht nur „Code“. Betrieb ist entscheidend: Backup/Restore, Aktualisierungen, Migrationen, Monitoring und Skalierung.
API/Backend → DB (Transaktionen, Constraints) → optional Cache (Reads) → optional Read Replica (Reporting). Wichtig: Das Datenmodell ist die „Wahrheit“ – nicht der Cache.
SQL ist die Sprache. Die Datenbank ist das System, das Daten speichert und SQL ausführt (z. B. PostgreSQL, MySQL, SQL Server).
Wenn du sehr flexible Schemas brauchst, extrem hohe Schreiblasten oder spezielle Modelle (Dokument/Key-Value/Graph). Bei starken Beziehungen und Konsistenz ist SQL oft die beste Wahl.
Normalisierung ist der sichere Start. Denormalisierung macht man gezielt, wenn Messungen zeigen, dass es nötig ist – und dann bewusst mit Index-/Cache-Strategie.
Nur mit Parametern/Prepared Statements arbeiten. Nutzer-Input nie in SQL-Strings zusammenbauen. Rollen so einschränken, dass selbst im Worst Case wenig Schaden möglich ist.
Weil Filter nicht selektiv genug sind, Funktionen/CAST auf der Spalte liegen oder der Planner einen Scan als günstiger einschätzt. EXPLAIN zeigt dir, was passiert.