Um SQL sicher mit KI zu schreiben, gibst du dem Agenten dein echtes Schema und einen Datenbankbenutzer mit Nur-Lese-Rechten, lässt dir den Query-Plan zeigen und hältst jede Migration klein, umkehrbar und auf einer Kopie der Daten getestet, bevor sie auch nur in die Nähe der Produktion kommt. Ein KI-Coding-Agent schreibt Abfragen und Migrationen schnell. Das Risiko ist nicht, dass er ab und zu schlechtes SQL schreibt, sondern dass ein schlechtes Statement dort läuft, wo es Schaden anrichten kann.
Diese Anleitung zeigt, wie du Abfragen per Prompt anforderst, wie du sie mit EXPLAIN prüfst, wie du nach Indizes fragst, wie du Migrationen schreibst, die nicht sperren und keine Daten verlieren, wo ORMs ins Spiel kommen und wie du einen Agenten per MCP mit einer Datenbank verbindest, ohne ihm die Schlüssel zur Produktion zu geben. Die Beispiele nutzen PostgreSQL; die Ideen lassen sich auf MySQL und SQLite übertragen.
Mit dem Schema anfangen, nicht mit der Frage
Der häufigste Grund für eine falsche Abfrage ist, dass der Agent geraten hat: eine Spalte namens user_id, die in Wirklichkeit account_id heißt, ein Status, der als Integer gespeichert ist, ein Soft-Delete-Flag, von dem er nichts wusste. Gib ihm zuerst das Schema.
Woher du es bekommst:
- Schemadateien des ORM.
prisma/schema.prisma, deine Drizzle-Schemadateien, Djangomodels.py, Railsdb/schema.rb. Der Agent kann sie direkt lesen; verweise im Prompt darauf. - Ein Dump nur der Struktur.
pg_dump --schema-onlyschreibt Tabellen, Spalten, Indizes und Constraints ohne Daten:
pg_dump --schema-only --no-owner "$DEV_DATABASE_URL" > schema.sql
- Eine Datenbankverbindung mit Nur-Lese-Rechten über einen MCP-Server, damit der Agent Tabellen selbst auflisten und Spalten ansehen kann (mehr dazu unten).
Ergänze dann, was ein Schema nicht verrät: ungefähre Tabellengrößen, welche Spalten oft NULL sind und was die Fachbegriffe bedeuten. „Ein aktiver Kunde ist einer mit einer Bestellung in den letzten 90 Tagen“ erspart eine falsche Vermutung.
Ein Prompt für eine Abfrage:
Using schema.sql: write a PostgreSQL query that returns, for each country,
the number of active customers (an order in the last 90 days) and their
total order value this year. Exclude soft-deleted customers
(deleted_at IS NOT NULL). orders has about 20M rows, customers 400k.
Run it on the dev database, show me the first 10 rows and the EXPLAIN plan.
Einen Nur-Lese-Benutzer für den Agenten nutzen
Bevor ein Agent irgendetwas gegen eine Datenbank ausführt, gib ihm einen eigenen Benutzer, der keine Daten ändern kann. Ab PostgreSQL 14 macht die eingebaute Rolle pg_read_all_data das kurz:
CREATE ROLE agent_ro LOGIN PASSWORD 'change-me';
GRANT pg_read_all_data TO agent_ro;
ALTER ROLE agent_ro SET default_transaction_read_only = on;
ALTER ROLE agent_ro SET statement_timeout = '30s';
pg_read_all_data gewährt Lesezugriff auf alle Tabellen, ohne Schreibrechte. Die beiden ALTER ROLE-Zeilen sichern doppelt ab: Transaktionen starten im Nur-Lese-Modus, und eine außer Kontrolle geratene Abfrage wird nach 30 Sekunden abgebrochen. Bei älteren Versionen oder für engeren Zugriff vergibst du stattdessen CONNECT, USAGE auf das Schema und SELECT auf die gewünschten Tabellen.
Richte diesen Benutzer auf eine Entwicklungs- oder Staging-Datenbank mit realistischen Daten ein, nicht auf die Produktion. Brauchst du Produktionszahlen, nutze ein Read-Replica oder eine aktuelle anonymisierte Kopie.
Den Query-Plan lesen
Eine Abfrage, die die richtigen Zeilen liefert, kann trotzdem Minuten dauern. Bitte den Agenten, für alles, was eine große Tabelle berührt, EXPLAIN auszuführen und dir den Plan zu erklären:
EXPLAIN (ANALYZE, BUFFERS)
SELECT c.country, count(DISTINCT c.id)
FROM customers c
JOIN orders o ON o.customer_id = c.id
WHERE o.created_at > now() - interval '90 days'
AND c.deleted_at IS NULL
GROUP BY c.country;
EXPLAIN allein zeigt den Plan, den die Datenbank verwenden würde. ANALYZE führt die Abfrage tatsächlich aus und ergänzt echte Laufzeiten und Zeilenzahlen. Das ist wichtig: EXPLAIN ANALYZE auf einem UPDATE oder DELETE ändert wirklich Daten. Pack solche Statements in eine Transaktion, die du zurückrollst, oder führe sie nur auf einer Kopie aus.
Worauf der Agent achten soll:
- Sequential Scans auf großen Tabellen, wo du auf wenige Zeilen filterst.
- Geschätzte und tatsächliche Zeilen weit auseinander, ein Hinweis auf veraltete Statistiken (
ANALYZE tablenameaktualisiert sie). - Sortierungen oder Hashes, die auf die Platte auslagern.
- Nested Loops über viele Zeilen, wo ein Hash Join günstiger wäre.
Ein guter Folge-Prompt: „Erkläre diesen Plan in einfachen Worten. Welcher Schritt braucht die meiste Zeit und warum? Schlag eine Änderung vor und zeig den neuen Plan.“
Nach Indizes fragen, samt Abwägung
Agenten schlagen gern Indizes vor. Jeder beschleunigt manche Lesezugriffe, verlangsamt jeden Schreibzugriff auf diese Tabelle und kostet Speicherplatz. Frag nach der Begründung:
The query above does a sequential scan on orders. Suggest at most one
index, explain which queries it helps and what it costs on writes,
and show the EXPLAIN plan with it on the dev database.
Auf einer stark genutzten Tabelle baust du den Index, ohne Schreibzugriffe zu blockieren:
CREATE INDEX CONCURRENTLY idx_orders_customer_created
ON orders (customer_id, created_at);
CONCURRENTLY dauert länger, blockiert aber keine Inserts und Updates, während es läuft. Es kann nicht innerhalb eines Transaktionsblocks laufen. Das ist wichtig, weil viele Migrationstools jede Migration in eine Transaktion packen; prüf, wie dein Tool damit umgeht (die meisten haben eine Option, die Transaktion für eine einzelne Migration abzuschalten). Schlägt ein nebenläufiger Build fehl, bleibt ein ungültiger Index zurück, den du löschen und neu bauen musst.
Migrationen schreiben, die sicher laufen
Eine Migration läuft gegen echte Daten, oft während die App Traffic bedient. Das sind die Muster, die du von deinem Agenten verlangen und in seiner Ausgabe prüfen solltest.
Expand, Migrate, Contract
Benenne nicht in einem Schritt um und lösche nicht in einem Schritt. Um users.name in users.full_name umzubenennen:
- Expand: Füge
full_nameals nullable Spalte hinzu. Rolle Code aus, der in beide Spalten schreibt. - Migrate: Fülle
full_namein Batches ausnamenach. Stelle Lesezugriffe auffull_nameum. - Contract: Lösche
namein einem späteren Release, sobald nichts mehr davon liest.
Jeder Schritt lässt sich für sich ausrollen und zurückrollen. Ein einzelnes ALTER TABLE ... RENAME COLUMN macht jede laufende Instanz des alten Codes in dem Moment kaputt, in dem es ausgeführt wird.
Lange Sperren vermeiden
Manche DDL-Befehle nehmen eine Sperre, die Lese- oder Schreibzugriffe blockiert, solange sie laufen. Bitte den Agenten, diese zu markieren und die sicherere Form zu verwenden:
| Riskant | Sicherer |
|---|---|
CREATE INDEX auf einer großen Tabelle | CREATE INDEX CONCURRENTLY |
| Foreign Key in einem Schritt hinzufügen | ADD CONSTRAINT ... NOT VALID, dann VALIDATE CONSTRAINT in einem eigenen Schritt |
SET NOT NULL auf einer großen Tabelle | Ein CHECK (col IS NOT NULL) NOT VALID hinzufügen, validieren, dann NOT NULL setzen |
| Spaltentyp ändern | Neue Spalte hinzufügen, nachfüllen, umstellen, alte löschen |
Ein riesiges UPDATE als Backfill | Batches von einigen tausend Zeilen |
Seit PostgreSQL 11 schreibt das Hinzufügen einer Spalte mit konstantem Default die Tabelle nicht neu, das ist also meist günstig. Setz außerdem ganz oben in der Migration ein Lock-Timeout, damit sie schnell fehlschlägt, statt sich hinter einer langen Abfrage einzureihen und alles dahinter zu blockieren:
SET lock_timeout = '5s';
Umkehrbar machen
Jede Migration braucht einen Weg zurück, entweder eine Down-Migration oder einen schriftlichen Rollback-Schritt. Fordere ihn ausdrücklich an: „Schreib die Up- und Down-Migration und sag mir, welche Schritte sich nicht ohne Datenverlust rückgängig machen lassen.“ Das Löschen einer Spalte kann keine Down-Migration umkehren; die Daten sind weg. Ein weiterer Grund, in einem späteren, eigenen Schritt nach einem Backup zu löschen.
Auf einer Kopie testen
Führe die Migration vor der Produktion auf einer Kopie der Daten aus: einer Staging-Datenbank, die aus einem aktuellen Backup wiederhergestellt wurde, oder einer lokalen Datenbank mit Fake-Daten in Produktionsgröße. Prüfe drei Dinge: dass sie durchläuft, wie lange sie dauert und dass die App mit dem neuen Schema noch funktioniert. Führe dann die Down-Migration aus und prüfe auch sie.
Mit einem ORM: generieren lassen, vom Agenten prüfen lassen
Wenn du ein ORM nutzt, ist der sicherste Ablauf: Modell ändern, das ORM die Migration erzeugen lassen und den Agenten das erzeugte SQL prüfen lassen.
| ORM | Migration erzeugen | Einspielen | SQL ansehen |
|---|---|---|---|
| Prisma | npx prisma migrate dev --name add_invoice --create-only | npx prisma migrate deploy | die erzeugte Datei migration.sql |
| Drizzle | npx drizzle-kit generate | npx drizzle-kit migrate | die SQL-Dateien in deinem Migrationsordner |
| Django | python manage.py makemigrations | python manage.py migrate | python manage.py sqlmigrate app 0004 |
| Rails | bin/rails generate migration AddFullNameToUsers | bin/rails db:migrate | db/schema.rb und die Migrationsdatei |
Prismas --create-only schreibt die Migration, ohne sie einzuspielen, sodass du das SQL vorher bearbeiten kannst, zum Beispiel um CONCURRENTLY hinzuzufügen oder einen Schritt aufzuteilen. Drizzle hat außerdem drizzle-kit push, das Schemaänderungen direkt ohne Migrationsdateien einspielt; praktisch für lokale Prototypen, für alles Geteilte nutzt du aber erzeugte Migrationen.
Ein Review-Prompt, der mit jedem dieser Tools funktioniert:
Read the new migration in prisma/migrations/. For each statement, tell me:
does it lock the table, can it lose data, how long will it take on a
table with 20M rows, and is there a safer form? Don't change any files.
Einen Agenten per MCP mit einer Datenbank verbinden
Ein MCP-Server lässt den Agenten selbst auf die Datenbank zugreifen: Tabellen auflisten, ein SELECT ausführen, einen Plan lesen. Das ist nützlich, und genau dort liegt auch das Risiko, also fang mit Nur-Lese-Rechten an.
Supabase betreibt einen gehosteten MCP-Server. Mit Stand Oktober 2026 führt read_only=true in der URL jede Abfrage als Postgres-Benutzer mit Nur-Lese-Rechten aus, und project_ref bindet ihn an ein Projekt:
claude mcp add --transport http supabase \
"https://mcp.supabase.com/mcp?project_ref=<your-project-ref>&read_only=true"
Supabase selbst rät, ein Produktionsprojekt nur dann anzubinden, wenn die Aufgabe wirklich Produktionsdaten braucht, und dann mit Projektbindung und Nur-Lese-Modus; die manuelle Freigabe von Tool-Aufrufen eingeschaltet zu lassen; und Ergebnisse zu prüfen, bevor man darauf handelt, weil der Schutz gegen Prompt Injection nicht lückenlos ist.
Reines PostgreSQL: Nutze einen gepflegten Postgres-MCP-Server mit deinem Benutzer agent_ro im Connection-String. Die besten MCP-Server fürs Programmieren listet Optionen und ihre Nur-Lese-Einstellungen auf. Der alte Postgres-Referenzserver des MCP-Projekts ist archiviert.
Die Regeln, die du in deine CLAUDE.md oder AGENTS.md schreiben solltest:
## Database
- The database MCP server is read-only. Never ask for write access.
- Schema changes go through migrations in prisma/migrations, never direct DDL.
- Never run UPDATE or DELETE without a WHERE clause.
- Run new queries with EXPLAIN on the dev database before proposing them.
Gib einem Agenten nie Schreibzugriff auf die Produktion
Das ist die eine Regel, die du nicht beugen solltest. Datenzeilen können Text enthalten, der wie eine Anweisung klingt (ein Support-Ticket, eine Nutzerbiografie), und ein Agent, der sie liest, folgt ihr womöglich; das ist Prompt Injection. Eine missverstandene Anfrage kann zu einem DELETE mit falschem Filter werden. Mit einem Nur-Lese-Benutzer ist der schlimmste Fall eine langsame Abfrage. Mit Schreibzugriff ist es ein Ausfall oder verlorene Daten.
Die sichere Aufteilung: Der Agent schreibt und testet SQL gegen Entwicklungsdaten; du prüfst es; ein Migrationstool oder ein Mensch spielt es in die Produktion ein. Sicherheit von KI-Coding-Agenten behandelt Sandboxing und Berechtigungen breiter.
So geht es in AgentVera
AgentVera ist eine Desktop-App für macOS, Windows und Linux, die Claude Code, Codex und andere CLI-Agenten nebeneinander ausführt. Einige Funktionen sind genau für diese Arbeit gebaut:
- Datenbank-Manager: Verbindungen zu PostgreSQL, MySQL/MariaDB und SQLite im selben Fenster wie deine Agenten, mit bearbeitbarer Tabellenansicht, einem SQL-Editor mit
EXPLAIN, einem Tabellen-Designer, der die DDL vor dem Anwenden zeigt, und einem ER-Diagramm. Als nur lesend markierte Verbindungen werden auch serverseitig nur lesend geöffnet, und eine als Produktion markierte Verbindung wird rot angezeigt, wobei Änderungen zuerst eine Bestätigung verlangen. Über das eingebaute Tool agentvera-db kannst du Agenten eine Verbindung nutzen lassen: Der Zugriff ist standardmäßig aus, startet beim Einschalten im Nur-Lese-Modus, Agenten sehen nie das Passwort, und jede Abfrage, die sie ausführen, wird protokolliert. Der Datenbank-Manager gehört zu den Tarifen Pro und Team. - DB Guard: Wenn ein Agent einen Turn beendet, werden geänderte Migrationsdateien auf Risiken geprüft, etwa
DROP,TRUNCATE,UPDATEoderDELETEohneWHERE,CREATE INDEXohneCONCURRENTLY, neue Constraints und fehlende Down-Schritte. Ist eine produktionsähnliche Verbindung ausgewählt, wird das Risiko anhand der echten Tabellengrößen gewichtet. Du kannst die Migration auf einer Schattendatenbank in einer Transaktion ausprobieren, die immer zurückgerollt wird, oder die Befunde zum Beheben an den Agenten zurückschicken. - MCP-Manager: Richte einen Datenbank-MCP-Server einmal ein, und er wird bei jedem Start an deine Agenten angebunden.
- Code-Review: Ein zweites Modell reviewt den Migrations- und Abfragecode, den der Agent geschrieben hat, wenn sein Turn endet.
Lade AgentVera herunter und probiere es auf einer Entwicklungsdatenbank aus.
Fazit
Gib dem Agenten das echte Schema und einen Nur-Lese-Benutzer auf Entwicklungsdaten. Bitte ihn, EXPLAIN auszuführen und den Plan zu erklären, jeden Index zu begründen und Migrationen in kleinen, umkehrbaren Expand-and-Contract-Schritten mit nicht blockierender DDL zu schreiben. Lass dein ORM Migrationen erzeugen und den Agenten sie prüfen, teste jede auf einer Kopie und behalte Schreibzugriffe auf die Produktion in deiner eigenen Hand. Klare Anweisungen machen all das leichter; Prompts für KI-Coding-Agenten schreiben geht ausführlicher auf Kontext, Pläne und Fertig-Kriterien ein.