- 1 Section
- 10 Lessons
- unbegrenzt
- SQL Fortgeschritten10
- 1.1Alle JOIN-Typen: RIGHT, FULL OUTER, CROSS, SELF
- 1.2Unterabfragen (Subqueries)
- 1.3Common Table Expressions (CTEs)
- 1.4Window Functions: ROW_NUMBER, RANK, PARTITION BY
- 1.5Views erstellen und nutzen
- 1.6Stored Procedures und Funktionen
- 1.7Trigger
- 1.8Datenbankindizes und Query-Optimierung
- 1.9Datenbankreporte und komplexe Auswertungen
- 1.10SQL-Fortgeschrittenenaufgaben
Datenbankindizes und Query-Optimierung
Eine SQL-Abfrage, die in der Entwicklungsumgebung sofort antwortet, kann in der Produktion mit Millionen Zeilen Minuten brauchen. Der Unterschied: Indizes. Sie sind das wichtigste Werkzeug für Datenbank-Performance.
Diese Lektion erklärt, was ein Index eigentlich ist, wie der dahinterstehende B-Tree funktioniert, welche Index-Typen es gibt (einspaltig, zusammengesetzt, eindeutig, Volltext), wann ein Index nicht hilft und wie du mit EXPLAIN herausfindest, was die Datenbank wirklich tut. Pflicht-Wissen für die IHK-Prüfung und für jeden, der mehr als nur Mini-Tabellen abfragt.
1) Das Grundproblem: Full Table Scan
Stell dir eine Tabelle kunden mit 1 Million Zeilen vor. Du suchst WHERE email = 'anna@example.com'. Was muss die Datenbank tun?
Ohne Index: jede einzelne Zeile lesen und prüfen. Das nennt man Full Table Scan – funktioniert, ist bei großen Tabellen aber sehr langsam. Bei 1 Million Zeilen liest die DB 1 Million Mal die Festplatte.
Mit Index: die DB schlägt direkt im Index nach, springt zum richtigen Datensatz – meist in unter einer Millisekunde. Das ist der Unterschied zwischen „eine Seite Buch durchblättern" und „im Stichwortverzeichnis nachschlagen".
2) Was ist ein Index?
Ein Index ist eine zusätzliche Datenstruktur, die die Werte einer Spalte (oder mehrerer Spalten) in sortierter Form speichert – zusammen mit einem Verweis (Pointer) auf die jeweilige Tabellen-Zeile. Vergleich:
- Tabelle: Liste aller Datensätze in der Reihenfolge des Einfügens
- Index: sortierte Liste der Werte einer Spalte mit Verweisen zurück zur Tabelle
Klassisches Analogon: Telefonbuch. Die Daten sind nach Nachname sortiert – das ist quasi der „Index". Wenn du jemanden nach Telefonnummer suchen wolltest, müsstest du das ganze Buch durchgehen. Mit einem zusätzlichen Index nach Nummer ginge auch das schnell.
3) Der B-Tree als interne Struktur
Die meisten Datenbanken speichern Indizes als B-Tree (englisch B-Tree, manchmal als „Balanced Tree" interpretiert). Das ist ein selbstbalancierender Baum mit mehreren Schlüsseln pro Knoten. Vereinfachte Darstellung für die Werte 1 bis 12:
4) Index Seek vs. Table Scan
Wenn du mit Index suchst, spricht man von Index Seek; ohne von Table Scan oder Full Table Scan. In den Ausführungsplänen tauchen diese Begriffe auf:
ORDER BY indizierte_spalte ohne WHERE. Auch das ist meist schneller als ein Table Scan, weil der Index oft kleiner ist als die Tabelle.5) Index erstellen, löschen, anzeigen
Die wichtigsten Befehle:
-- Einspaltigen Index erstellen CREATE INDEX idx_kunden_email ON kunden(email); -- Eindeutigen Index (verbietet Duplikate) CREATE UNIQUE INDEX idx_kunden_email ON kunden(email); -- Zusammengesetzter Index (mehrere Spalten) CREATE INDEX idx_best_kunde_datum ON bestellungen(kunde_id, bestelldatum); -- Vorhandene Indizes anzeigen (MySQL) SHOW INDEX FROM kunden; -- Index löschen DROP INDEX idx_kunden_email ON kunden; -- (PostgreSQL: DROP INDEX idx_kunden_email;)
Konvention für Index-Namen: idx_tabelle_spalte. Bei zusammengesetzten Indizes die Spalten anhängen: idx_bestellungen_kunde_datum.
6) Die wichtigsten Index-Typen
PRIMARY KEY automatisch angelegt. Eindeutig, NOT NULL. Eine Tabelle hat genau einen davon.MATCH … AGAINST (MySQL) oder tsvector (PostgreSQL). Andere Datenstruktur als B-Tree.7) Zusammengesetzte Indizes und ihre Reihenfolge
Bei einem Index über mehrere Spalten ist die Reihenfolge entscheidend:
CREATE INDEX idx_best ON bestellungen(kunde_id, bestelldatum);
Dieser Index hilft bei:
WHERE kunde_id = 1– ja, erste SpalteWHERE kunde_id = 1 AND bestelldatum >= '2026-01-01'– ja, beide SpaltenWHERE bestelldatum >= '2026-01-01'– nein (die erste Index-Spalte fehlt im Filter)
Faustregel: ein zusammengesetzter Index ist nur dann nutzbar, wenn der Filter von links die Index-Spalten enthält. Das nennt man Leftmost Prefix Rule.
kunde_id ist meist selektiver als status (mit nur 3–5 Werten). Außerdem: was du häufig in WHERE-Gleichheitsbedingungen nutzt, gehört nach vorne.
8) Wann ein Index sich lohnt – und wann nicht
Indizes sind nicht kostenlos. Sie kosten:
- Speicherplatz: jeder Index ist eine zusätzliche Datenstruktur
- Schreib-Performance: bei jedem
INSERT/UPDATE/DELETEmüssen alle Indizes der Tabelle aktualisiert werden - Wartungsaufwand: alte/unbenutzte Indizes müssen gepflegt werden
| Index lohnt sich, wenn … | Index lohnt sich NICHT, wenn … |
|---|---|
Spalte wird oft im WHERE abgefragt | Spalte wird kaum gefiltert |
| Tabelle ist groß (>10.000 Zeilen) | Tabelle ist klein (Full Table Scan ist schnell) |
| Spalte hat hohe Selektivität (viele unterschiedliche Werte) | Spalte hat wenige unterschiedliche Werte (z. B. boolesches Flag) |
| Lesevorgänge dominieren gegenüber Schreibvorgängen | Tabelle wird sehr häufig geschrieben (Logs, Queues) |
| Spalte ist Fremdschlüssel zu großer Tabelle | Spalte ist sehr breit (langer Text), erhöht Index-Größe stark |
9) EXPLAIN – die Wahrheit ans Licht bringen
Wie findest du heraus, ob die DB deinen Index wirklich nutzt? Mit EXPLAIN (in PostgreSQL: EXPLAIN ANALYZE). Du stellst dem SELECT einfach EXPLAIN voran:
EXPLAIN SELECT * FROM bestellungen WHERE kunde_id = 1;
Die Ausgabe variiert zwischen DBs. Bei MySQL etwa so:
type zeigt, wie zugegriffen wird (ALL = Table Scan, schlecht; ref/range/const/eq_ref = Index, gut). key nennt den verwendeten Index. rows schätzt, wie viele Zeilen gelesen werden müssen.10) Schlechte EXPLAIN-Werte erkennen
Auf welche Signale solltest du achten?
type = ALL(MySQL) oderSeq Scan(PostgreSQL) bei großen Tabellen → Full Table Scan, schlecht- Hohe
rows→ DB schätzt, dass sie viele Zeilen lesen muss key = NULL→ es wird kein Index genutztUsing filesort→ die DB muss sortieren ohne Index-Unterstützung (oft teuer)Using temporary→ temporäre Tabelle wird erzeugt (z. B. bei GROUP BY ohne Index-Unterstützung)
Diese Signale sind keine harten Fehler, sondern Hinweise. Bei einer kleinen Tabelle ist ein Table Scan völlig in Ordnung. Bei Millionen Zeilen mit häufigen Suchen aber ein Performance-Problem.
11) Wann Indizes ignoriert werden
Auch wenn ein Index existiert, kann die DB ihn übergehen:
- Funktionen auf der Spalte:
WHERE UPPER(email) = 'X'nutzt keinen normalen Index aufemail. Workaround: Function-Based Index oder gespeicherte Spalte mitUPPER(email) - Implizite Typkonvertierung:
WHERE id = '5'(String statt Int) kann den Index ausschalten - LIKE mit führendem Wildcard:
WHERE name LIKE '%Müller'kann den Index nicht nutzen (nur'Müller%'geht) ORüber mehrere Spalten:WHERE a = 1 OR b = 2oft schwer indizierbar – ggf. zwei separate Indizes nötig- Niedrige Selektivität: wenn die WHERE-Bedingung auf 80 % der Tabelle zutrifft, ist Table Scan billiger als Index Seek + Random Reads
WHERE status != 'aktiv' oder WHERE x NOT IN (…) kann Indizes erschweren. Negationen schließen viele Werte ein, die DB liest dann oft trotzdem alles. Lieber positiv formulieren, wenn möglich.
12) Query-Optimierung – allgemeine Tipps
Indizes sind das wichtigste, aber nicht das einzige Werkzeug. Weitere Hebel:
- Nur benötigte Spalten holen:
SELECT a, bstattSELECT *. Spart Bandbreite und ermöglicht Covering Indexes (Index enthält alle gefragten Spalten) - Filter früh anwenden:
WHERE-Bedingungen reduzieren das Datenvolumen vor Joins - JOINs gezielt einsetzen: jede JOIN-Spalte sollte einen Index haben. Siehe L1 JOINs
LIMIT: holt nicht mehr, als du brauchst- Statistiken aktuell halten:
ANALYZE TABLE(MySQL) bzw.ANALYZE(PostgreSQL) lässt die DB Selektivitäts-Statistiken aktualisieren – wichtig nach großen Datenänderungen - Komplexe Abfragen aufteilen: in eine CTE oder mehrere Schritte; manchmal ist die DB damit besser dran
- Aggregate vor JOINs: wenn du große Tabellen vorab aggregierst und dann erst joinst, sparst du oft viel
13) Wartung von Indizes
Indizes „verschleißen" mit der Zeit – durch viele Updates entstehen Lücken im B-Tree, der Index wird ineffizient:
-- MySQL: Index neu aufbauen OPTIMIZE TABLE bestellungen; -- PostgreSQL: Index neu aufbauen REINDEX TABLE bestellungen; -- Statistiken aktualisieren (PostgreSQL) ANALYZE bestellungen;
In normalen Anwendungen reicht oft ein nächtlicher Wartungs-Job. Bei DBs mit sehr hohem Schreibaufkommen ist regelmäßige Wartung wichtig.
14) Unbenutzte Indizes finden
Indizes, die niemand nutzt, kosten nur (Speicher + Schreib-Performance), ohne etwas zu bringen. Die meisten DBs bieten Statistiken, welche Indizes wie oft verwendet wurden – in MySQL z. B. über performance_schema oder sys.schema_unused_indexes, in PostgreSQL über pg_stat_user_indexes. Bei einer Performance-Analyse sind solche „Karteileichen" der erste Aufräumkandidat.
15) Häufige Fehler bei Indizes
- Zu viele Indizes: jede Spalte indizieren bremst Schreibvorgänge enorm und bringt selten Vorteile
- Zu wenige Indizes: bei großen Tabellen führt das zu Full Table Scans bei jeder Abfrage
- Index auf falsche Spalte: ein Index auf einer Spalte mit nur 2–3 unterschiedlichen Werten (z. B.
geschlecht) ist meist nutzlos - Index-Hint missbrauchen:
USE INDEX (…)erzwingt einen Index – meist ist der Optimizer schlauer als du - EXPLAIN ignoriert: Performance-Optimierung ohne EXPLAIN ist Raten
- Indizes auf Fremdschlüsseln vergessen: bei JOINs werden diese ständig genutzt – fehlende Indizes hier sind ein häufiger Grund für lahme Abfragen
UNIQUE und normalem Index, oder welche SQL-Aussagen zu lahmen Abfragen führen. Wer EXPLAIN einmal in einem realen Projekt eingesetzt hat, kann das mühelos beantworten.
Zusammenfassung
Ein Index ist eine sortierte Hilfsstruktur (meist B-Tree), die einer Datenbank erlaubt, Datensätze in logarithmischer Zeit zu finden – statt jede Zeile zu prüfen. Wichtige Typen: Primärschlüssel-Index (automatisch), UNIQUE Index, normaler Index, zusammengesetzter Index (Reihenfolge entscheidend: Leftmost Prefix Rule) und Volltextindex. Indizes kosten Speicher und bremsen Schreibvorgänge – nur indexieren, was wirklich gefragt wird. Mit EXPLAIN prüfst du, ob die DB deinen Index nutzt; ein type = ALL oder key = NULL deutet auf einen Full Table Scan hin. Indizes greifen nicht, wenn Funktionen auf der Spalte stehen (UPPER(x)), LIKE mit führendem Wildcard verwendet wird oder die Selektivität zu niedrig ist.
