Indizes & Performance
Lerne, wie Indizes deine SQL-Abfragen um ein Vielfaches beschleunigen - wann du sie brauchst und wie du sie richtig einsetzt.
Wenn deine Datenbank waechst, werden Abfragen langsamer. Indizes sind das wichtigste Werkzeug, um die Performance deiner Abfragen dramatisch zu verbessern - manchmal von Minuten auf Millisekunden.
Was ist ein Index?
Stell dir ein Buch ohne Inhaltsverzeichnis vor. Um ein bestimmtes Thema zu finden, muesste man jede Seite durchblaettern. Ein Index (Inhaltsverzeichnis) zeigt dir sofort, auf welcher Seite das Thema steht.
Genauso funktioniert ein Datenbank-Index: Statt alle Zeilen zu durchsuchen, findet die Datenbank die gesuchten Daten direkt.
Ohne Index
-- Die Datenbank muss ALLE Zeilen durchsuchen (Full Table Scan)
SELECT * FROM kunden WHERE email = 'anna@example.com';
-- Bei 1 Million Kunden: Alle 1 Million Zeilen pruefen
Mit Index
-- Index auf die email-Spalte erstellen
CREATE INDEX idx_kunden_email ON kunden(email);
-- Jetzt findet die Datenbank den Kunden direkt
SELECT * FROM kunden WHERE email = 'anna@example.com';
-- Bei 1 Million Kunden: Nur wenige Lookups noetig
Index erstellen
CREATE INDEX index_name ON tabellenname(spalte);
Einfacher Index
-- Index auf eine Spalte
CREATE INDEX idx_kunden_stadt ON kunden(stadt);
-- Jetzt sind Abfragen nach Stadt schnell:
SELECT * FROM kunden WHERE stadt = 'Berlin';
Zusammengesetzter Index (Composite Index)
-- Index auf mehrere Spalten
CREATE INDEX idx_bestellungen_kunde_datum
ON bestellungen(kunden_id, bestelldatum);
-- Beschleunigt Abfragen, die nach Kunde UND Datum filtern:
SELECT * FROM bestellungen
WHERE kunden_id = 1 AND bestelldatum >= '2026-01-01';
UNIQUE Index
-- Eindeutiger Index (verhindert Duplikate + beschleunigt Suche)
CREATE UNIQUE INDEX idx_kunden_email_unique ON kunden(email);
Ein UNIQUE Index ist automatisch auch ein normaler Index. Wenn du einen UNIQUE Constraint auf eine Spalte setzt, erstellt die Datenbank automatisch einen UNIQUE Index.
Wann brauche ich einen Index?
Index SINNVOLL fuer:
| Situation | Beispiel |
|---|---|
| WHERE-Bedingungen | WHERE stadt = 'Berlin' |
| JOIN-Spalten | ON b.kunden_id = k.id |
| ORDER BY | ORDER BY bestelldatum DESC |
| Spalten mit UNIQUE Constraint | email UNIQUE |
| Fremdschluessel | kunden_id REFERENCES kunden(id) |
| Haeufig gesuchte Spalten | WHERE status = 'offen' |
Index NICHT sinnvoll fuer:
| Situation | Grund |
|---|---|
| Sehr kleine Tabellen | Full Table Scan ist schneller |
| Spalten, die selten in WHERE stehen | Index wird nicht genutzt |
| Spalten mit wenigen verschiedenen Werten | z.B. geschlecht (nur M/W/D) |
| Tabellen, die hauptsaechlich geschrieben werden | Index verlangsamt INSERT/UPDATE |
EXPLAIN - Ausfuehrungsplan analysieren
EXPLAIN zeigt dir, wie die Datenbank deine Abfrage ausfuehrt:
-- PostgreSQL:
EXPLAIN SELECT * FROM kunden WHERE stadt = 'Berlin';
-- PostgreSQL mit tatsaechlicher Ausfuehrungszeit:
EXPLAIN ANALYZE SELECT * FROM kunden WHERE stadt = 'Berlin';
-- SQLite:
EXPLAIN QUERY PLAN SELECT * FROM kunden WHERE stadt = 'Berlin';
Ergebnis ohne Index (PostgreSQL)
Seq Scan on kunden (cost=0.00..1.06 rows=2 width=...)
Filter: (stadt = 'Berlin'::text)
Seq Scan = Sequential Scan = Alle Zeilen werden durchsucht (langsam bei vielen Daten).
Ergebnis mit Index (PostgreSQL)
Index Scan using idx_kunden_stadt on kunden (cost=0.14..8.16 rows=2 width=...)
Index Cond: (stadt = 'Berlin'::text)
Index Scan = Der Index wird benutzt (schnell).
Arten von Indizes
B-Tree Index (Standard)
Der Standard-Index, gut fuer Vergleiche (=, <, >, <=, >=, BETWEEN):
CREATE INDEX idx_produkte_preis ON produkte(preis);
-- Nutzt den Index:
SELECT * FROM produkte WHERE preis < 50;
SELECT * FROM produkte WHERE preis BETWEEN 20 AND 80;
SELECT * FROM produkte ORDER BY preis;
Hash Index (PostgreSQL)
Schneller als B-Tree fuer Gleichheitsvergleiche, aber nur dafuer:
-- PostgreSQL:
CREATE INDEX idx_kunden_email_hash ON kunden USING HASH (email);
-- Nutzt den Index (nur =):
SELECT * FROM kunden WHERE email = 'anna@example.com';
-- Nutzt den Index NICHT:
SELECT * FROM kunden WHERE email LIKE 'anna%';
Partial Index (Teilindex)
Ein Index, der nur einen Teil der Daten umfasst:
-- Index nur fuer offene Bestellungen
CREATE INDEX idx_bestellungen_offen
ON bestellungen(bestelldatum)
WHERE status = 'offen';
-- Wird genutzt bei:
SELECT * FROM bestellungen WHERE status = 'offen' AND bestelldatum > '2026-03-01';
Partial Indexes sind kleiner und schneller, weil sie nicht alle Zeilen indexieren.
Expression Index
Index auf berechneten Ausdruecken:
-- PostgreSQL: Index auf Kleinbuchstaben-E-Mail
CREATE INDEX idx_kunden_email_lower ON kunden(LOWER(email));
-- Wird genutzt bei:
SELECT * FROM kunden WHERE LOWER(email) = 'anna@example.com';
Performance-Tipps
1. Die richtigen Spalten indexieren
-- Index auf die Spalten, die in WHERE, JOIN und ORDER BY stehen
CREATE INDEX idx_bestellungen_kunden_id ON bestellungen(kunden_id);
CREATE INDEX idx_bestellungen_status ON bestellungen(status);
CREATE INDEX idx_bestellpositionen_bestell_id ON bestellpositionen(bestell_id);
CREATE INDEX idx_bestellpositionen_produkt_id ON bestellpositionen(produkt_id);
2. Composite Index Reihenfolge beachten
-- Index auf (stadt, name)
CREATE INDEX idx_kunden_stadt_name ON kunden(stadt, name);
-- Nutzt den Index (stadt ist erste Spalte):
SELECT * FROM kunden WHERE stadt = 'Berlin';
SELECT * FROM kunden WHERE stadt = 'Berlin' AND name LIKE 'A%';
-- Nutzt den Index NICHT (name ohne stadt):
SELECT * FROM kunden WHERE name LIKE 'A%';
Regel: Ein zusammengesetzter Index wird nur genutzt, wenn die Abfrage von links nach rechts die Spalten des Index verwendet.
3. Index-Nutzung verhindern (Anti-Patterns)
-- Diese Abfragen koennen den Index NICHT nutzen:
-- Funktion auf indizierter Spalte:
SELECT * FROM kunden WHERE UPPER(stadt) = 'BERLIN';
-- Negation:
SELECT * FROM kunden WHERE stadt != 'Berlin';
-- LIKE mit fuehrendem Platzhalter:
SELECT * FROM kunden WHERE name LIKE '%Schmidt';
-- OR auf verschiedenen Spalten:
SELECT * FROM kunden WHERE stadt = 'Berlin' OR name = 'Max';
4. Zu viele Indizes vermeiden
Indizes haben auch Nachteile:
-- Jeder Index verlangsamt INSERT, UPDATE und DELETE!
-- Weil der Index bei jeder Aenderung aktualisiert werden muss.
| Operation | Ohne Index | Mit Index |
|---|---|---|
| SELECT (mit WHERE) | Langsam | Schnell |
| INSERT | Schnell | Langsamer |
| UPDATE | Schnell | Langsamer |
| DELETE | Schnell | Langsamer |
| Speicherplatz | Wenig | Mehr |
Faustregel: Indexiere nur Spalten, die haeufig in WHERE, JOIN und ORDER BY verwendet werden.
Index loeschen
DROP INDEX idx_kunden_stadt;
-- Sicherer:
DROP INDEX IF EXISTS idx_kunden_stadt;
Praxisbeispiel: Shop-Datenbank optimieren
-- Wichtigste Indizes fuer unseren Online-Shop:
-- 1. Kunden nach Stadt suchen
CREATE INDEX idx_kunden_stadt ON kunden(stadt);
-- 2. Bestellungen nach Kunde finden (FK Index)
CREATE INDEX idx_bestellungen_kunden_id ON bestellungen(kunden_id);
-- 3. Bestellungen nach Status filtern
CREATE INDEX idx_bestellungen_status ON bestellungen(status);
-- 4. Bestellungen nach Datum sortieren
CREATE INDEX idx_bestellungen_datum ON bestellungen(bestelldatum);
-- 5. Bestellpositionen schnell finden
CREATE INDEX idx_bp_bestell_id ON bestellpositionen(bestell_id);
CREATE INDEX idx_bp_produkt_id ON bestellpositionen(produkt_id);
-- 6. Produkte nach Kategorie filtern
CREATE INDEX idx_produkte_kategorie ON produkte(kategorie);
Was kommt als Naechstes?
Im naechsten Tutorial lernst du Window Functions kennen - eine fortgeschrittene SQL-Technik fuer komplexe Analysen und Rankings.
Zusammenfassung
- Indizes beschleunigen SELECT-Abfragen dramatisch
- Erstelle Indizes auf Spalten in WHERE, JOIN und ORDER BY
- EXPLAIN zeigt dir, ob ein Index genutzt wird
- Composite Indexes folgen der Links-nach-Rechts-Regel
- Partial Indexes indexieren nur relevante Daten
- Zu viele Indizes verlangsamen Schreiboperationen
- Faustregel: Index fuer lesehaeufige, nicht fuer schreibhaeufige Spalten
Uebungen
- Index erstellen: Erstelle einen Index auf der
kategorie-Spalte der Produkttabelle. - EXPLAIN: Vergleiche den Ausfuehrungsplan einer Abfrage vor und nach dem Erstellen eines Index.
- Composite Index: Erstelle einen zusammengesetzten Index auf
(kunden_id, bestelldatum)in der Bestellungen-Tabelle. Fuer welche Abfragen ist er nuetzlich? - Partial Index: Erstelle einen Index, der nur offene Bestellungen umfasst.
- Analyse: Welche Indizes wuerdest du fuer eine Blog-Datenbank mit den Tabellen
posts,kommentareundtagserstellen?
-- Loesung zu Uebung 1:
CREATE INDEX idx_produkte_kategorie ON produkte(kategorie);
-- Loesung zu Uebung 4 (PostgreSQL):
CREATE INDEX idx_offene_bestellungen
ON bestellungen(bestelldatum)
WHERE status = 'offen';
Pro-Tipp: Nutze EXPLAIN ANALYZE regelmaessig, um langsame Abfragen zu identifizieren. In PostgreSQL kannst du mit SET log_min_duration_statement = 1000; alle Abfragen loggen, die laenger als 1 Sekunde dauern. So findest du die Stellen, wo Indizes den groessten Unterschied machen.