Fortgeschritten 13 minSQL

Text-to-SQL lokal: eigene Datenbank in Sprache abfragen naturel

Text-to-SQL mit einem lokalen LLM ermöglicht es, eine Frage auf Französisch zu stellen („quel est le chiffre d'affaires par région le mois dernier ?“) und eine SQL-Abfrage zu erhalten, die sich auf Ihrer PostgreSQL- oder MySQL-Datenbank ausführen lässt — ohne dass das Schema oder die Daten Ihre Infrastruktur verlassen. Dieser Leitfaden behandelt die konkreten Abläufe: das Schema in den Kontext einfügen, eine Python-Pipeline mit Ollama aufbauen und vor allem Schutzmaßnahmen (Nur-Lese-Zugriff, Validierung, Begrenzungen) einrichten, ohne die keine Text-to-SQL-Lösung in der Produktion einsetzbar ist.

Von Mohamed Meguedmi·Aktualisierung 2026-08-27·Unter Windows, macOS und Linux getestet

#Warum Text-to-SQL mit einem lokalen LLM nutzen?

Cloud-Lösungen für Text-to-SQL (BI-Assistenten, Data-Warehouse-Copiloten) senden Ihr Schema – Tabellen- und Spaltennamen, manchmal auch Stichproben von Zeilen – an einen Server eines Drittanbieters. Bei einer Kunden-, Personal- oder Finanzdatenbank ist das oft ein Ausschlusskriterium: Schon das Schema allein verrät die Struktur Ihres Geschäfts, und die Stichproben enthalten personenbezogene Daten.

Ein lokales LLM löst dieses Problem an der Wurzel: Das Modell läuft über Ollama auf Ihrem Rechner, das Schema bleibt im lokalen Speicher, und die generierte Abfrage wird in Ihrer Datenbank ausgeführt, ohne dass ein einziges Byte über das Internet übertragen wird. Die Nutzung ist außerdem kostenlos und unabhängig von sämtlichen API-Ratenlimits.

Datenschutz
Schema und Daten verlassen niemals Ihr Netzwerk — das erleichtert die Einhaltung der DSGVO und den Schutz von Geschäftsgeheimnissen.
Kosten
Keine Kosten pro Anfrage. Ein Datenanalyst kann Hunderte von Iterationen durchführen, ohne Rechnung zu erhalten.
Zugänglichkeit
Fachanwender, die SQL nicht kennen, fragen die Datenbank in natürlicher Sprache ab.
Kontrolle
Sie bestimmen das Modell, den Prompt und die Schutzmaßnahmen – ohne eine Blackbox auf einem entfernten Server.
!
Text-to-SQL ist nicht magisch
Ein LLM erzeugt plausibles SQL, dessen Korrektheit jedoch nicht garantiert ist. Bei komplexen Schemata (mehreren Joins, mehrdeutigen Spalten) treten weiterhin Fehler auf. Behandeln Sie die Ausgabe als Vorschlag, der geprüft werden muss, niemals als verlässliche Grundlage – insbesondere, wenn eine Person ohne technischen Hintergrund darauf ihre Entscheidungen stützt.

#Wie funktioniert das konkret

Das Kit „Copilote Local“

Dieser Guide führt Sie zum Modell. Das Kit führt Sie zum Copiloten, der in Ihrem Editor Code schreibt.

  • Lebenslanger Online-Zugang
  • PDF + Dateien
  • Erstattung binnen 30 Tagen

Das Prinzip von Text-to-SQL mit einem LLM umfasst drei Schritte. Zuerst beschreibt man dem Modell das Datenbankschema (die DDL der relevanten Tabellen). Anschließend übermittelt man ihm die Frage des Nutzers mit einer strikten Anweisung: ausschließlich eine SQL-Abfrage für den Zieldialekt erzeugen. Schließlich übernimmt man die Abfrage, validiert sie und führt sie im Nur-Lese-Modus aus.

  1. 01
    Introspektion des Schemas
    Man extrahiert die Tabellenstruktur (Spalten, Typen, Schlüssel) aus der Datenbank – automatisch statt von Hand, damit sie mit der Datenbank synchron bleibt.
  2. 02
    Promptkonstruktion
    Ein Systemprompt wird zusammengestellt, der den SQL-Dialekt, das relevante Schema und die Regeln enthält (nur SELECT, LIMIT verpflichtend, keine Kommentare).
  3. 03
    Generierung
    Das lokale LLM gibt eine Abfrage zurück. Diese wird bereinigt (eventuell vorhandene Markdown-Codeblock-Markierungen wie ```sql werden entfernt).
  4. 04
    Validierung + Ausführung
    Wir prüfen, ob es sich tatsächlich um eine SELECT-Anweisung handelt, führen sie mit einer Datenbankrolle mit ausschließlich Leserechten aus und geben die Zeilen zurück.

#Voraussetzungen

Ollama installiert
Der Daemon muss auf http://localhost:11434 lauschen. Prüfen Sie dies mit „ollama ps“.
Ein leistungsfähiges Modell
Ein neueres Code-Modell (Qwen3-Coder 30B-A3B, Devstral 24B) liefert bei SQL deutlich bessere Ergebnisse als ein kleines Allzweckmodell (siehe Abschnitt Modelle).
Python 3.10+
Mit dem passenden Datenbank-Client: psycopg2-binary (PostgreSQL) oder PyMySQL (MySQL).
Ein schreibgeschützter Datenbankzugriff
Idealerweise eine dedizierte SQL-Rolle, die ausschließlich SELECT-Anweisungen ausführen darf – die wichtigste Schutzmaßnahme.
Terminal
# Récupérer un modèle adapté au SQL
ollama pull qwen3-coder:30b

# Dépendances Python
pip install ollama psycopg2-binary sqlparse

#Das Schema Ihrer Datenbank dem Modell übergeben

Dieser Schritt bestimmt 80 % der Ergebnisqualität. Das Modell kann nur dann eine korrekte Abfrage generieren, wenn es die genauen Namen der Tabellen und Spalten, ihre Datentypen und die Beziehungen zwischen ihnen kennt. Zwei Ansätze: das DDL unverändert einfügen oder die Datenbank per Introspektion untersuchen, um eine kompakte Beschreibung zu erstellen.

Bei einer kleinen Datenbank (weniger als etwa zwanzig Tabellen) kann man alles in den Kontext aufnehmen. Darüber hinaus überschreitet das Schema den nutzbaren Kontext und überfordert das Modell: Dann müssen die für die Frage relevanten Tabellen ausgewählt werden (über einen ersten Suchdurchlauf oder eine fachliche Zuordnung). Hier folgt eine PostgreSQL-Introspektion, die ein für das LLM lesbares Schema erzeugt.

schema.py
import psycopg2

def get_schema(conn):
    """Retourne le schéma sous forme de CREATE TABLE simplifiés."""
    query = """
        SELECT table_name, column_name, data_type
        FROM information_schema.columns
        WHERE table_schema = 'public'
        ORDER BY table_name, ordinal_position;
    """
    tables = {}
    with conn.cursor() as cur:
        cur.execute(query)
        for table, col, dtype in cur.fetchall():
            tables.setdefault(table, []).append(f"{col} {dtype}")

    lines = []
    for table, cols in tables.items():
        cols_str = ", ".join(cols)
        lines.append(f"TABLE {table} ({cols_str});")
    return "\n".join(lines)
→
Fügen Sie fachliche Kommentare hinzu
Eine Spalte namens „ca_ht“ ist für das Modell mehrdeutig. Ergänzen Sie das Schema um Anmerkungen: „ca_ht (Umsatz ohne Steuern, in Euro)“. Diese wenigen Wörter reduzieren Fehler bei der Spaltenauswahl drastisch. In PostgreSQL lassen sich mit COMMENT ON COLUMN hinterlegte Kommentare über information_schema und pg_description abrufen.

#Vollständige Python-Pipeline mit Ollama

Hier ist eine minimale, aber funktionsfähige Pipeline: Schema → Prompt → Generierung → Bereinigung → Validierung → Ausführung. Sie verwendet den offiziellen Python-Client von Ollama und eine Datenbankrolle mit ausschließlich lesendem Zugriff.

text_to_sql.py
import re
import ollama
import psycopg2
import sqlparse

MODEL = "qwen3-coder:30b"

SYSTEM_PROMPT = """Tu es un expert PostgreSQL. Génère UNE seule requête SQL
qui répond à la question de l'utilisateur, en respectant ces règles :
- Uniquement des requêtes SELECT (jamais INSERT/UPDATE/DELETE/DROP).
- Utilise exactement les noms de tables et colonnes du schéma fourni.
- Ajoute toujours LIMIT 100 si la question ne précise pas de limite.
- Réponds UNIQUEMENT avec le SQL, sans explication ni balise Markdown.

Schéma de la base :
{schema}"""

def generate_sql(question, schema):
    resp = ollama.chat(
        model=MODEL,
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT.format(schema=schema)},
            {"role": "user", "content": question},
        ],
        options={"temperature": 0},  # déterminisme : crucial pour du SQL
    )
    return clean_sql(resp["message"]["content"])

def clean_sql(raw):
    # Retire les fences Markdown ```sql ... ``` si le modèle en ajoute
    raw = re.sub(r"```(?:sql)?", "", raw).strip()
    return raw.rstrip(";") + ";"

Der Ausführungsteil trennt bewusst die Validierung vom Datenbankaufruf. Alles, was nicht genau eine SELECT-Anweisung ist, wird abgelehnt, noch bevor der Cursor geöffnet wird.

text_to_sql.py (Fortsetzung)
def is_read_only(sql):
    statements = sqlparse.parse(sql)
    if len(statements) != 1:
        return False  # une seule requête, pas d'empilement
    stmt = statements[0]
    if stmt.get_type() != "SELECT":
        return False
    forbidden = ("insert", "update", "delete", "drop",
                 "alter", "truncate", "grant", "create")
    lowered = sql.lower()
    return not any(kw in lowered for kw in forbidden)

def run_query(sql):
    if not is_read_only(sql):
        raise ValueError(f"Requête refusée (non lecture seule) : {sql}")
    # Rôle 'readonly' : ne dispose QUE du privilège SELECT côté base
    conn = psycopg2.connect(
        dbname="analytics", user="readonly",
        password="...", host="localhost",
    )
    with conn.cursor() as cur:
        cur.execute("SET statement_timeout = '5s';")  # anti-requête folle
        cur.execute(sql)
        cols = [d[0] for d in cur.description]
        rows = cur.fetchall()
    conn.close()
    return cols, rows

if __name__ == "__main__":
    from schema import get_schema
    ro = psycopg2.connect(dbname="analytics", user="readonly",
                          password="...", host="localhost")
    schema = get_schema(ro)
    question = "Combien de commandes par mois en 2025 ?"
    sql = generate_sql(question, schema)
    print("SQL généré :", sql)
    cols, rows = run_query(sql)
    print(cols)
    for r in rows:
        print(r)
i
temperature = 0
Stellen Sie die Temperatur für Text-to-SQL immer auf 0 ein. Hier ist keine Kreativität gefragt, sondern die wahrscheinlichste und reproduzierbare Abfrage. Eine hohe Temperatur führt zu Variationen bei Spalten und Joins, die die Ausführung scheitern lassen.

#Generiertes SQL zuverlässiger und sicherer machen

Dieser Abschnitt unterscheidet eine Demo von einem echten Deployment. Ein LLM kann eine destruktive Abfrage erzeugen, wenn es dazu aufgefordert wird – oder versehentlich durch eine Injection in der Frage. Die Absicherung darf sich niemals allein auf den Prompt stützen: Sie muss auf mehreren Ebenen auf Datenbankseite erfolgen.

  1. 01
    Datenbankrolle mit ausschließlich lesendem Zugriff (wichtigste Schutzmaßnahme)
    Erstellen Sie eine SQL-Rolle, die NUR über die SELECT-Berechtigung verfügt. Selbst wenn das Modell einen DROP TABLE-Befehl generiert, lehnt die Datenbank ihn ab. Das ist der einzige wirklich zuverlässige Schutzmechanismus: „GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;“ und nichts weiter.
  2. 02
    Anwendungsvalidierung
    Parsen Sie den SQL-Code vorab mit sqlparse und weisen Sie alles zurück, was nicht genau eine SELECT-Anweisung ist. Zusammen mit der Datenbankrolle bildet dies eine doppelte Schutzbarriere.
  3. 03
    Anfragezeitüberschreitung
    SET statement_timeout verhindert, dass eine fehlerhaft aufgebaute Abfrage (kartesisches Produkt über Millionen von Zeilen) die Datenbank überlastet.
  4. 04
    LIMIT erzwingen
    Erzwingen Sie ein LIMIT sowohl im Prompt ALS AUCH im Code, damit niemals ganze Tabellen in den Speicher geladen werden.
  5. 05
    Korrektur-Schleife
    Wenn die Ausführung einen SQL-Fehler zurückgibt, geben Sie die Fehlermeldung an das Modell weiter und fordern Sie eine korrigierte Abfrage an (maximal 1 oder 2 Versuche).
PostgreSQL-Rolle mit ausschließlich Leserechten
-- À exécuter une fois par un admin
CREATE ROLE readonly WITH LOGIN PASSWORD '...';
GRANT CONNECT ON DATABASE analytics TO readonly;
GRANT USAGE ON SCHEMA public TO readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
-- Les tables créées plus tard héritent aussi du SELECT
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT SELECT ON TABLES TO readonly;
!
Die Frage niemals in SQL interpolieren
Die Frage des Benutzers kommt in den Prompt des LLM und wird niemals durch Zeichenkettenverkettung in eine SQL-Abfrage eingefügt. Ausgeführt wird der vom Modell erzeugte und validierte SQL-Code, unverändert über cur.execute(sql) und ohne eingeschleuste Benutzerparameter. Das klassische Risiko einer SQL-Injection verlagert sich damit auf die Prüfung, ob ausschließlich Lesezugriffe erfolgen – daher ist die Datenbankrolle so wichtig.

Die Korrekturschleife verbessert die Erfolgsquote deutlich. Viele Fehler sind trivial (ein leicht falscher Spaltenname, eine dialektspezifische Datumsfunktion), und das Modell korrigiert sie beim zweiten Versuch, wenn es die Fehlermeldung der Datenbank-Engine sieht.

Korrektur-Schleife
def answer(question, schema, max_retries=2):
    sql = generate_sql(question, schema)
    for attempt in range(max_retries + 1):
        try:
            return sql, run_query(sql)
        except Exception as e:
            if attempt == max_retries:
                raise
            # On renvoie l'erreur au modèle pour correction
            fix_prompt = (
                f"La requête suivante a échoué :\n{sql}\n\n"
                f"Erreur PostgreSQL : {e}\n"
                f"Corrige la requête. SQL uniquement."
            )
            resp = ollama.chat(model=MODEL, messages=[
                {"role": "system", "content": SYSTEM_PROMPT.format(schema=schema)},
                {"role": "user", "content": fix_prompt},
            ], options={"temperature": 0})
            sql = clean_sql(resp["message"]["content"])

#Welche lokalen Modelle sind besonders gut in SQL?

SQL ist eine Programmieraufgabe: Spezialisierte „Coder“-Modelle sind Allzweckmodellen gleicher Größe deutlich überlegen. 2026 macht ein aktuelles Codemodell wie Qwen3-Coder 30B-A3B den entscheidenden Unterschied – kleine Modelle mit 2B bis 8B bekommen einfache Abfragen noch zusammen, scheitern aber, sobald mehrere Joins oder eine Fensteraggregation nötig sind. (Codestral 22B, lange für SQL empfohlen, steht inzwischen unter einer Lizenz, die den Einsatz in Produktionsumgebungen ausschließt: Für Unternehmen kommt es daher nicht infrage.)

Qwen3-Coder 30B-A3B
Die Standardwahl 2026. MoE für Code mit 3 Milliarden aktiven Parametern: schnell, 256k Kontext für große Schemata, ≈19 GB in Q4 auf einer RTX 4090 oder einem aktuellen Mac. Lizenz: Apache 2.0.
Devstral 24B
Code-Spezialist von Mistral AI (Apache 2.0), ≈14 GB in Q4 – passt auf eine Karte mit 16 GB wie die RTX 4080. Der beste Kompromiss für SQL auf einer bescheiden ausgestatteten Workstation.
Qwen 3.8 27B
Aktuelles Allround-Modell mit soliden Schlussfolgerungsfähigkeiten bei komplexen Joins (≈18 GB, 262k Kontext, Bildverarbeitung). Stellen Sie die Einstellung für das Schlussfolgern auf „low“: Bei einer so strukturierten Aufgabe wie SQL neigt das Modell mit der Standardeinstellung dazu, zu lange nachzudenken.
Kleine Modelle 2B–8B (Qwen 3.5 4B, Granite 4.2 8B)
Nur für sehr einfache Schemata und direkte Fragen. Zu vermeiden, sobald die Datenbank nicht triviale Beziehungen enthält.
→
Quantisierung Q4_K_M
Für Text-to-SQL bietet Q4_K_M das beste Verhältnis zwischen Qualität und VRAM-Bedarf. Der Genauigkeitsverlust gegenüber Q8 ist bei dieser strukturierten Aufgabe vernachlässigbar, während die VRAM-Einsparung den Wechsel zu einem größeren Modell ermöglicht — und die Modellgröße ist für die Korrektheit des SQL deutlich wichtiger als die Quantisierung.

#Fehlerbehebung

Das Modell erfindet Spalten
Das Schema ist unvollständig oder zu groß. Beschränken Sie es auf die relevanten Tabellen und ergänzen Sie fachliche Kommentare zu mehrdeutigen Spalten.
Antworten mit zusätzlichem Text vor oder nach dem SQL
Verstärken Sie die Anweisung „Nur SQL, keine Erklärung“ und behalten Sie das Entfernen der Markdown-Codeblock-Begrenzungen in clean_sql bei.
Fehler bei Datumsfunktionen
Geben Sie im Systemprompt den SQL-Dialekt an (PostgreSQL und MySQL unterscheiden sich bei DATE_TRUNC, YEAR() usw.). Die Korrekturschleife fängt die übrigen Fehler ab.
Langsame Anfragen oder Anfragen mit Zeitüberschreitung
statement_timeout erfüllt seinen Zweck. Ergänzen Sie den Prompt für große Tabellen um „immer auf einen angemessenen Datumsbereich filtern“.
« Connection refused » Ollama
Der Daemon ist nicht gestartet. Überprüfen Sie „ollama ps“ und stellen Sie sicher, dass der Dienst unter http://localhost:11434 auf Verbindungen wartet.

#Weiterführende Informationen

Text-to-SQL verwendet mehrere Bausteine wieder, die bereits auf der Website behandelt wurden. Diese Leitfäden knüpfen an diesen Leitfaden an:

Ollama in eine Python-Anwendung über die REST-API integrieren
Um diese Pipeline über eine FastAPI-API bereitzustellen sowie Streaming und den JSON-Modus zu verwalten.
Function Calling und strukturierte JSON-Ausgaben mit Ollama
Eine Alternative, die eine strukturierte Ausgabe (Abfrage + Erklärung) garantiert, statt sie durch nachträgliche Textbereinigung herzustellen.
Quantisierung wählen (Q4, Q5, Q8, FP16)
Um die Größe des SQL-Modells mit dem verfügbaren VRAM Ihrer Grafikkarte abzustimmen.
Hat Ihnen dieser Guide geholfen?

Haben Sie Feedback, einen Fehler entdeckt oder möchten Sie etwas präzisieren? Geben Sie uns Bescheid – so wird der Guide für alle besser.