SQL Tuning für Einsteiger – Explain, Indexe und Joins

SQL Tuning für Einsteiger - Explain, Indexe und Joins - IT-Glossary

Deine Abfrage läuft, aber zu langsam. Die Datenbank arbeitet hart, doch die Antwortzeit passt nicht. Bevor du Hardware aufrüstest, lohnt sich ein Blick auf Explain, Indexe und saubere Joins. Schon kleine Anpassungen bringen oft große Effekte.

Dieser Guide erklärt dir Schritt für Schritt, wie du Query-Pläne liest, passende Indexe setzt und Joins so formulierst, dass der Optimizer leichtes Spiel hat. Alles in einfacher Sprache, mit praxisnahen Beispielen, damit du direkt loslegen kannst.

Wie ein Datenbank-Optimizer denkt

Ein Optimizer schätzt Kosten: Wie viele Zeilen werden gelesen, welche Indexe passen, welches Join-Verfahren ist am schnellsten. Grundlage sind Statistiken über die Verteilung deiner Werte. Je besser diese Statistiken, desto klüger der Plan. Halte sie mit Befehlen wie ANALYZE aktuell.

Explain lesen – was der Plan dir sagt

Explain Basics

Ein Explain-Plan zeigt die Schritte der Abfrage. Achte auf:

  • Zugriffsart: Seq Scan/Table Scan vs Index Scan
  • Filter: Bedingungen, die Zeilen aussortieren
  • Joins: Nested Loop, Hash Join, Merge Join
  • Schätzwerte: Zeilenanzahl, Kosten, manchmal Zeit

Beispiel PostgreSQL:

EXPLAIN ANALYZE
SELECT o.id, c.name
FROM orders o
JOIN customers c ON c.id = o.customer_id
WHERE o.created_at >= date_trunc('month', now());

Siehst du Seq Scan auf einer großen Tabelle mit Zeitfilter, fehlt oft ein Index auf created_at oder ein kombinierter Index.

Häufige Aha-Momente

Wenn Zeilenschätzungen stark vom Ist abweichen, sind Statistiken alt oder der Filter unglücklich. Aktualisiere Stats und prüfe Selektivität deiner Bedingungen. Eine Bedingung, die nur wenige Zeilen übrig lässt, ist indexfreundlich.

Indexe – die große Wirkung mit wenig Aufwand

B-Tree als Standard

Der B-Tree-Index beschleunigt Vergleiche wie =, <, >, BETWEEN und ORDER BY. Er ist dein Allrounder für Primärschlüssel, Fremdschlüssel, Zeitstempel und Sortierungen.

-- schneller filtern nach Datum
CREATE INDEX idx_orders_created_at ON orders(created_at);

Zusammengesetzte Indexe

Bei mehreren Spalten entscheidet die Reihenfolge. Setze das selektivste Feld nach vorn oder wähle die Reihenfolge passend zur Abfrage.

-- häufig: Kunde + aktueller Zeitraum
CREATE INDEX idx_orders_customer_created
ON orders(customer_id, created_at);

Wichtig: Ein Index auf (customer_id, created_at) hilft customer_id allein, aber nicht automatisch nur created_at.

Covering Indexe

Wenn alle benötigten Spalten im Index stehen, kann die DB die Tabelle nicht mehr lesen. In PostgreSQL mit INCLUDE, in MySQL über zusätzliche Indexspalten.

-- Lookup ohne Tabellenzugriff
CREATE INDEX idx_orders_lookup
ON orders(customer_id, created_at) INCLUDE(total_amount);

Partielle Indexe

Wenn du immer wieder die gleichen Teilmengen abfragst, nutze partielle Indexe.

CREATE INDEX idx_orders_open
ON orders(customer_id)
WHERE status = 'open';

Typische Index-Fallen

  • Gemischte Datentypen (Text vs Zahl) verhindern Matches
  • Funktionen wie LOWER(col) brauchen Funktionsindexe
  • LIKE ‚%abc‘ ohne Präfix hilft B-Tree nicht – nutze Volltext oder Trigramme

Joins optimieren – korrekt verbinden, richtig filtern

Join-Verfahren verstehen

  • Nested Loop: gut, wenn äußere Menge klein und innerer Zugriff indexiert ist
  • Hash Join: stark bei großen Mengen ohne passende Sortierung
  • Merge Join: effizient, wenn beide Seiten sortiert sind oder Indexe die Sortierung liefern

Du musst das Verfahren nicht erzwingen. Gib dem Optimizer die Indexe und Filter, die ihn dahin führen.

Filter früh anwenden

Schreibe Bedingungen so, dass sie vor dem Join greifen können.

-- besser
SELECT ...
FROM orders o
JOIN customers c ON c.id = o.customer_id
WHERE o.created_at >= '2025-01-01';

-- schlechter: Filter erst nach großem Join
SELECT ...
FROM orders o
JOIN customers c ON c.id = o.customer_id
-- ohne Datum im WHERE: mehr Daten schieben

ON vs WHERE

Semantik zählt: In INNER JOIN ist es egal. Bei LEFT JOIN gehören join-spezifische Bedingungen in ON, damit du linke Zeilen nicht versehentlich entfernst.

-- korrekt bei LEFT JOIN
SELECT c.id, o.id
FROM customers c
LEFT JOIN orders o
  ON o.customer_id = c.id
  AND o.created_at >= '2025-01-01';

EXISTS statt IN

EXISTS ist oft schneller und klarer, wenn du Existenz prüfen willst.

SELECT c.id
FROM customers c
WHERE EXISTS (
  SELECT 1 FROM orders o
  WHERE o.customer_id = c.id
  AND o.created_at >= '2025-01-01'
);

Pagination ohne Bremse

OFFSET liest erst, dann verwirft es Zeilen. Besser ist Keyset Pagination mit einem stabilen Sortierschlüssel.

-- schneller: seek pagination
SELECT id, created_at
FROM orders
WHERE created_at > :last_seen
ORDER BY created_at
LIMIT 50;

Schreibstil der Abfrage – kleine Regeln mit großer Wirkung

Nur holen, was du brauchst

SELECT * lädt unnötige Spalten. Greife gezielt zu, dann passen mehr Daten in Cache und Indexe wirken besser.

Prädikate sargable machen

Schreibe Bedingungen, die den Index nutzen können.

-- sargable
WHERE created_at >= '2025-01-01'

-- nicht sargable
WHERE date(created_at) >= '2025-01-01'  -- Funktion auf Spalte verhindert Index

CTEs und Views

CTEs werden je nach DB materialisiert oder inline. Materialisierung kann bremsen. Prüfe mit Explain, ob die CTE wirklich hilft oder ob eine Subquery besser ist.

Wartung und Statistiken – die Basis für gute Pläne

Halte Statistiken frisch (ANALYZE), säubere Tabellen mit VACUUM/OPTIMIZE, prüfe Index-Bloat und entferne unbenutzte Indexe. Ohne aktuelle Stats geraten Kostenschätzungen daneben und gute Indexe werden ignoriert.

Mini-Fahrplan für deinen ersten Tuning-Run

  1. Wähle eine langsame Abfrage.
  2. Explain Analyze ausführen und den teuersten Schritt identifizieren.
  3. Passenden Index setzen oder Filter umformulieren.
  4. Plan erneut prüfen, Laufzeit messen, Änderung behalten oder verwerfen.
  5. Zum nächstteuren Schritt gehen und wiederholen.

Fazit

Gute Performance ist kein Zufall. Wenn du Explain-Pläne liest, passende Indexe wählst und Joins bewusst formulierst, gewinnst du oft Sekunden pro Abfrage zurück. Arbeite iterativ, halte Statistiken aktuell und denke an saubere Prädikate. So werden deine SQL-Abfragen verlässlich schnell und bleiben gut wartbar.

0 Kommentare

Hinterlasse einen Kommentar

An der Diskussion beteiligen?
Hinterlasse uns deinen Kommentar!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert