🐘 Datenbank & SQL

PostgreSQL mit Claude Code: Fortgeschrittene Queries und Performance 2026

📅 5. Mai 2026 ⏱ 11 min Lesezeit 👤 SpockyMagicAI Team

PostgreSQL ist die leistungsstärkste Open-Source-Datenbank der Welt — und mit Claude Code wird das Schreiben komplexer, performanter Queries zum Kinderspiel. In diesem Guide zeigen wir, wie du 2026 Window Functions, CTEs, JSONB, Partial Indexes und Full-Text Search mit KI-Unterstützung meisterst.

🔮 Window Functions

ROW_NUMBER, RANK, LAG/LEAD für Analytics

Window Functions berechnen Werte über eine Menge von Zeilen, ohne sie zu gruppieren. Ideal für Rankings, gleitende Durchschnitte und Vergleiche mit Vorperioden.

-- Claude Code Prompt: "Schreib eine Query mit ROW_NUMBER, RANK und LAG für Umsatz-Analytics" WITH monthly_revenue AS ( SELECT DATE_TRUNC('month', created_at) AS month, product_id, SUM(amount) AS revenue FROM orders WHERE status = 'completed' GROUP BY 1, 2 ) SELECT month, product_id, revenue, -- Rang innerhalb des Monats nach Umsatz RANK() OVER (PARTITION BY month ORDER BY revenue DESC) AS rank_in_month, -- Fortlaufende Zeilennummer global ROW_NUMBER() OVER (ORDER BY month, revenue DESC) AS row_num, -- Umsatz des Vormonats (LAG) LAG(revenue) OVER (PARTITION BY product_id ORDER BY month) AS prev_month_revenue, -- Umsatz des Folgemonats (LEAD) LEAD(revenue) OVER (PARTITION BY product_id ORDER BY month) AS next_month_revenue, -- Wachstum in Prozent vs Vormonat ROUND( (revenue - LAG(revenue) OVER (PARTITION BY product_id ORDER BY month)) / NULLIF(LAG(revenue) OVER (PARTITION BY product_id ORDER BY month), 0) * 100, 2 ) AS growth_pct FROM monthly_revenue ORDER BY month DESC, rank_in_month;
💡 Claude Code Tipp: Beschreibe einfach deine Business-Frage: "Zeig mir den Umsatz jedes Produkts im Vergleich zum Vormonat mit Wachstumsrate." Claude generiert die korrekte Window Function mit PARTITION BY und ORDER BY automatisch.
🔄 CTEs

Common Table Expressions: Rekursive Abfragen

CTEs machen komplexe Queries lesbar und ermöglichen rekursive Abfragen — z. B. für Baumstrukturen wie Kategorien, Mitarbeiterhierarchien oder Graph-Traversal.

-- Claude Code Prompt: "Traversiere eine Kategorie-Hierarchie rekursiv in PostgreSQL" WITH RECURSIVE category_tree AS ( -- Anker: Wurzelkategorien (ohne parent) SELECT id, name, parent_id, 0 AS depth, ARRAY[id] AS path, name::TEXT AS full_path FROM categories WHERE parent_id IS NULL UNION ALL -- Rekursiver Teil: Kinder der bisherigen Knoten SELECT c.id, c.name, c.parent_id, ct.depth + 1, ct.path || c.id, ct.full_path || ' > ' || c.name FROM categories c JOIN category_tree ct ON c.parent_id = ct.id WHERE ct.depth < 10 -- Zyklen verhindern ) SELECT REPEAT(' ', depth) || name AS indented_name, full_path, depth FROM category_tree ORDER BY path;

✅ Lesbarkeit

Komplexe Subqueries werden als benannte Blöcke strukturiert — leichter zu debuggen und zu warten.

🔁 Rekursion

WITH RECURSIVE traversiert Bäume und Graphen ohne externe Loops — direkt in SQL.

♻️ Wiederverwendung

Ein CTE kann mehrfach in derselben Query referenziert werden — ohne doppelte Berechnung.

🗂 JSONB

JSONB Spalten: Flexibles Schema in PostgreSQL

JSONB speichert semi-strukturierte Daten binär-optimiert. Perfekt für Event-Logs, Konfigurationen und flexible Metadaten — ohne separate NoSQL-Datenbank.

-- Claude Code Prompt: "JSONB Spalte für User-Events mit Index und Query" -- Tabelle mit JSONB-Spalte anlegen CREATE TABLE user_events ( id BIGSERIAL PRIMARY KEY, user_id UUID NOT NULL, event_type TEXT NOT NULL, payload JSONB NOT NULL DEFAULT '{}', created_at TIMESTAMPTZ DEFAULT NOW() ); -- GIN-Index für effiziente JSONB-Suche CREATE INDEX idx_events_payload ON user_events USING GIN (payload); -- Spezifischer Index für häufig abgefragten Key CREATE INDEX idx_events_plan ON user_events ((payload ->> 'plan')); -- JSONB-Daten einfügen INSERT INTO user_events (user_id, event_type, payload) VALUES (gen_random_uuid(), 'signup', '{"plan": "trial", "source": "blog", "features": ["ai", "sql"]}'), (gen_random_uuid(), 'upgrade', '{"plan": "pro", "amount": 49, "currency": "EUR"}'); -- JSONB abfragen: Operator -> (JSON), ->> (Text) SELECT user_id, payload ->> 'plan' AS plan, (payload ->> 'amount')::NUMERIC AS amount, payload -> 'features' AS features_array FROM user_events WHERE payload @> '{"plan": "trial"}' -- Containment-Operator nutzt GIN-Index AND payload ? 'source' -- Key-Existenz-Check AND created_at > NOW() - INTERVAL '30 days';
💡 JSONB vs JSON: Immer JSONB verwenden! Es wird beim Schreiben geparst und binär gespeichert — Queries sind 10–100x schneller als auf JSON-Spalten. Der GIN-Index macht @> Containment-Queries blitzschnell.
📍 Indexes

Partial Indexes und Expression Indexes

Nicht jede Zeile verdient einen Index-Eintrag. Partial Indexes indizieren nur relevante Teilmengen — kleiner, schneller, wartungsfreundlicher.

-- Claude Code Prompt: "Erstelle Partial und Expression Indexes für Orders-Tabelle" -- Partial Index: Nur aktive, nicht abgeschlossene Bestellungen CREATE INDEX idx_orders_pending ON orders (created_at, user_id) WHERE status IN ('pending', 'processing'); -- → 95% aller Rows werden ignoriert; Index bleibt winzig -- Expression Index: Für Case-Insensitive E-Mail-Suche CREATE INDEX idx_users_email_lower ON users (LOWER(email)); -- Query MUSS denselben Ausdruck nutzen damit Index greift: SELECT * FROM users WHERE LOWER(email) = LOWER('User@Example.com'); -- Partial + Expression kombiniert: Aktive Premium-User nach Domain CREATE INDEX idx_premium_users_domain ON users (SPLIT_PART(email, '@', 2)) WHERE plan = 'premium' AND deleted_at IS NULL; -- BRIN Index für Zeitserien (sehr klein, gut für append-only Tabellen) CREATE INDEX idx_events_time_brin ON user_events USING BRIN (created_at) WITH (pages_per_range = 128); -- Index-Größen und Nutzung prüfen SELECT schemaname, tablename, indexname, pg_size_pretty(pg_relation_size(indexrelid)) AS index_size, idx_scan AS times_used FROM pg_stat_user_indexes ORDER BY idx_scan DESC;
🔍 EXPLAIN ANALYZE

Query-Optimierung verstehen

EXPLAIN ANALYZE zeigt den tatsächlichen Ausführungsplan mit Laufzeiten — der einzig zuverlässige Weg, Performance-Probleme zu diagnostizieren.

-- Claude Code Prompt: "Analysiere diese Query mit EXPLAIN ANALYZE und erkläre Bottlenecks" EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) SELECT u.email, COUNT(o.id) AS order_count, SUM(o.amount) AS total_spent FROM users u JOIN orders o ON u.id = o.user_id WHERE o.created_at > NOW() - INTERVAL '90 days' GROUP BY u.id, u.email HAVING SUM(o.amount) > 1000 ORDER BY total_spent DESC LIMIT 20;

🔴 Seq Scan

PostgreSQL liest die gesamte Tabelle. Oft ein Zeichen für fehlende Indexes — oder sehr kleine Tabellen.

🟢 Index Scan

Nur relevante Rows werden gelesen. Optimal für selektive WHERE-Bedingungen auf indizierten Spalten.

🟡 Hash Join

Effizient für große Joins. Teuer wenn Tabelle nicht in den Work_mem passt — dann Disk-Spills!

-- Häufige Bottlenecks mit Claude Code identifizieren -- Prompt: "Was bedeutet dieser EXPLAIN-Plan, wo ist der Flaschenhals?" -- Praktische Diagnose-Queries: -- 1) Langsamste Queries finden (pg_stat_statements nötig) SELECT LEFT(query, 100) AS query_preview, ROUND(mean_exec_time::NUMERIC, 2) AS avg_ms, calls, ROUND(total_exec_time::NUMERIC / 1000, 2) AS total_sec FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10; -- 2) Tabellen ohne passende Indexes (hohe Seq-Scan-Rate) SELECT relname AS table_name, seq_scan, idx_scan, ROUND(seq_scan::NUMERIC / NULLIF(seq_scan + idx_scan, 0) * 100, 1) AS seq_pct FROM pg_stat_user_tables WHERE seq_scan + idx_scan > 100 ORDER BY seq_pct DESC;
💡 Claude Code Workflow: Kopiere deinen EXPLAIN-Output in Claude Code und schreibe: "Erkläre mir diesen Query-Plan und schlage konkrete Optimierungen vor." Claude erkennt Seq Scans, schlechte Join-Strategie und fehlende Indexes sofort.
🔎 Full-Text Search

Full-Text Search mit tsvector und tsquery

PostgreSQL hat eingebaute Volltextsuche auf Enterprise-Niveau — ohne Elasticsearch. tsvector indiziert Dokumente, tsquery formuliert Suchanfragen.

-- Claude Code Prompt: "Volltext-Suche für Artikel-Tabelle mit Ranking und Highlighting" -- Tabelle mit FTS-Spalte CREATE TABLE articles ( id BIGSERIAL PRIMARY KEY, title TEXT NOT NULL, content TEXT NOT NULL, author TEXT, published BOOLEAN DEFAULT FALSE, -- Generierte tsvector-Spalte (automatisch aktualisiert!) search_vec TSVECTOR GENERATED ALWAYS AS ( setweight(to_tsvector('german', COALESCE(title, '')), 'A') || setweight(to_tsvector('german', COALESCE(content, '')), 'B') || setweight(to_tsvector('german', COALESCE(author, '')), 'C') ) STORED ); -- GIN-Index für schnelle Volltextsuche CREATE INDEX idx_articles_fts ON articles USING GIN (search_vec); -- Partial Index: Nur veröffentlichte Artikel durchsuchen CREATE INDEX idx_articles_fts_published ON articles USING GIN (search_vec) WHERE published = TRUE; -- Suche mit Ranking und Highlighting SELECT id, title, author, -- Relevanz-Score (0–1) ts_rank(search_vec, query) AS relevance, -- Treffer-Highlighting im Content ts_headline( 'german', content, query, 'MaxWords=50, MinWords=20, StartSel=<mark>, StopSel=</mark>' ) AS excerpt FROM articles, to_tsquery('german', 'PostgreSQL & (Performance | Optimierung)') query WHERE search_vec @@ query AND published = TRUE ORDER BY relevance DESC LIMIT 10; -- Phrase-Suche und Wildcards SELECT * FROM articles WHERE search_vec @@ to_tsquery('german', 'claude:* & query:*') AND published = TRUE;

Fazit: Claude Code als PostgreSQL-Copilot

Die sechs Techniken in diesem Guide — Window Functions, rekursive CTEs, JSONB, Partial Indexes, EXPLAIN ANALYZE und Full-Text Search — decken 90% aller fortgeschrittenen PostgreSQL-Anforderungen ab. Mit Claude Code brauchst du keine Syntax auswendig zu lernen: Beschreibe dein Problem in natürlicher Sprache, erhalte produktionsreife SQL — und lass dir den EXPLAIN-Plan direkt erklären.

🚀 Schneller

Kein manuelles Index-Tuning mehr. Claude erkennt fehlende Indexes aus dem Query-Kontext.

🛡 Sicherer

Korrekte Parameterization, kein SQL-Injection-Risiko, NULLIF für Division-by-Zero.

📐 Lesbarer

CTEs statt verschachtelter Subqueries. Kommentare direkt im generierten Code.

PostgreSQL-Queries mit KI schreiben

Teste Claude Code 14 Tage kostenlos — kein Kreditkarte nötig. Schreib komplexe SQL-Abfragen in Sekunden.

Jetzt kostenlos starten →