Indizes in SQL: Wie du Abfragen gezielt beschleunigst

Wenn Abfragen mit wachsender Datenmenge immer langsamer werden, sind Indizes oft die Lösung. Du lernst, wie sie funktionieren, wann sie helfen und wie du mit EXPLAIN prüfst, ob sie wirklich greifen.

Teilen

Deine Anwendung lief am Anfang blitzschnell, doch mit jeder Woche werden dieselben Abfragen träger. Bei zehn Datensätzen fällt nichts auf, bei zehn Millionen wird das Warten zur Qual. In den allermeisten Fällen liegt die Ursache nicht an einer schlechten Datenbank, sondern an fehlenden Indizes. Ein Index ist das wichtigste Werkzeug, um Lesezugriffe schnell zu halten – und gleichzeitig eines der am häufigsten missverstandenen. In diesem Beitrag schauen wir uns an, wie Indizes funktionieren, wie du sie anlegst, wann sie wirklich helfen und wie du mit EXPLAIN überprüfst, ob sie greifen.

Was ist ein Index überhaupt?

Stell dir ein dickes Fachbuch ohne Stichwortverzeichnis vor. Um eine bestimmte Information zu finden, müsstest du jede Seite durchblättern. Genau das macht eine Datenbank bei einem Full Table Scan: Sie liest jede Zeile der Tabelle, um die passenden zu finden. Ein Index ist das Stichwortverzeichnis am Ende des Buchs – eine sortierte Datenstruktur (meist ein B-Baum), die für einen bestimmten Spaltenwert direkt auf die passende Zeile zeigt.

Nehmen wir eine typische Tabelle mit Nutzern:

CREATE TABLE users (
    id       INTEGER PRIMARY KEY,
    email    TEXT NOT NULL,
    country  TEXT,
    created  DATE
);

Die Spalte id ist der Primärschlüssel und wird automatisch indiziert. Suchst du aber nach der E-Mail-Adresse, muss die Datenbank ohne weiteren Index jede einzelne Zeile prüfen. Bei Millionen von Zeilen bedeutet das Millionen von Vergleichen.

Einen Index anlegen

Einen Index erstellst du mit CREATE INDEX. Du gibst ihm einen Namen und legst fest, für welche Spalte er gelten soll:

-- Beschleunigt die Suche nach E-Mail-Adressen
CREATE INDEX idx_users_email ON users (email);

-- Erzwingt zusätzlich Eindeutigkeit: keine doppelten E-Mails
CREATE UNIQUE INDEX idx_users_email_unique ON users (email);

Nach dem Anlegen kann eine Abfrage wie diese den Index nutzen, statt die ganze Tabelle zu durchsuchen:

SELECT id, country
FROM users
WHERE email = 'anna@example.com';

Die Datenbank springt jetzt über den sortierten Index direkt zum Treffer – statt Millionen Zeilen liest sie nur noch eine Handvoll. Ein UNIQUE-Index ist dabei besonders praktisch: Er sorgt nicht nur für Tempo, sondern verhindert auch, dass dieselbe E-Mail zweimal in der Tabelle landet.

Wann ein Index hilft – und wann nicht

Indizes helfen überall dort, wo Werte gesucht, gefiltert, sortiert oder verknüpft werden. Gute Kandidaten sind Spalten in WHERE-Bedingungen, in JOIN-Bedingungen und in ORDER BY. Es gibt aber Fallstricke, die einen vorhandenen Index unbrauchbar machen. Der häufigste: eine Funktion auf der indizierten Spalte.

-- Nutzt den Index NICHT: LOWER() verändert die Spalte
SELECT * FROM users
WHERE LOWER(email) = 'anna@example.com';

-- Nutzt den Index: die Spalte bleibt unangetastet
SELECT * FROM users
WHERE email = 'anna@example.com';

Sobald du die Spalte in eine Funktion packst, kann die Datenbank den sortierten Index nicht mehr verwenden und fällt auf den langsamen Full Table Scan zurück. Ähnliches gilt für ein führendes Wildcard bei LIKE:

-- Kann den Index nicht nutzen (beginnt mit %)
SELECT * FROM users WHERE email LIKE '%@example.com';

-- Kann den Index nutzen (fester Anfang)
SELECT * FROM users WHERE email LIKE 'anna%';

Wichtig ist außerdem: Jeder Index kostet etwas. Bei jedem INSERT, UPDATE und DELETE muss auch der Index aktualisiert werden. Auf einer Tabelle mit sehr vielen Schreibvorgängen bremsen zu viele Indizes das System aus. Faustregel: Indiziere gezielt die Spalten, nach denen du wirklich häufig suchst – nicht einfach alles.

Zusammengesetzte Indizes und die Reihenfolge der Spalten

Oft filterst du nach mehreren Spalten gleichzeitig. Dafür gibt es zusammengesetzte Indizes über mehrere Spalten. Entscheidend ist hier die Reihenfolge der Spalten im Index:

CREATE INDEX idx_users_country_created
ON users (country, created);

Dieser Index hilft bei Abfragen, die mit country beginnen – entweder allein oder zusammen mit created:

-- Nutzt den Index voll aus
SELECT * FROM users
WHERE country = 'DE' AND created >= '2026-01-01';

-- Nutzt den Index ebenfalls (nur die erste Spalte)
SELECT * FROM users WHERE country = 'DE';

-- Nutzt den Index NICHT effizient: created steht nicht vorn
SELECT * FROM users WHERE created >= '2026-01-01';

Man spricht hier vom Leftmost-Prefix-Prinzip: Ein zusammengesetzter Index lässt sich nur von links her nutzen. Fehlt die erste Spalte in der Bedingung, kann die Datenbank den Index nicht sinnvoll einsetzen. Stelle deshalb die Spalte nach vorn, die am häufigsten für sich allein gefiltert wird.

Mit EXPLAIN prüfen, ob der Index greift

Rate nie, ob ein Index verwendet wird – frag die Datenbank. Mit EXPLAIN (bzw. EXPLAIN QUERY PLAN in SQLite oder EXPLAIN ANALYZE in PostgreSQL) zeigt dir das System, wie es eine Abfrage ausführt:

EXPLAIN QUERY PLAN
SELECT id, country
FROM users
WHERE email = 'anna@example.com';

Ohne Index steht dort so etwas wie SCAN users – die ganze Tabelle wird gelesen. Mit passendem Index erscheint stattdessen ein Hinweis auf eine Indexsuche, etwa SEARCH users USING INDEX idx_users_email. In PostgreSQL achtest du auf den Unterschied zwischen Seq Scan (langsam, ganze Tabelle) und Index Scan (schnell, gezielter Zugriff). So siehst du schwarz auf weiß, ob dein Index tatsächlich seine Arbeit tut, bevor du eine langsame Abfrage in Produktion schickst.

Fazit

Indizes sind der Hebel mit dem größten Effekt, wenn Lesezugriffe langsam werden. Sie funktionieren wie ein Stichwortverzeichnis: Statt die ganze Tabelle zu durchblättern, springt die Datenbank direkt zum Treffer. Lege Indizes gezielt auf Spalten an, nach denen du oft filterst, sortierst oder verknüpfst, halte die Spalten frei von Funktionen, achte bei zusammengesetzten Indizes auf die Reihenfolge und überprüfe mit EXPLAIN, ob sie wirklich greifen. Vermeide es gleichzeitig, wahllos alles zu indizieren – jeder Index kostet Speicher und verlangsamt Schreibvorgänge. Wenn du dieses Gleichgewicht triffst, bleiben deine Abfragen auch mit Millionen von Zeilen schnell.