5. RetrievalFortgeschritten

SQL RAG: Datenbanken mit natürlicher Sprache abfragen

21. Juli 2026
20 min Lesezeit
Ailog Team

Implementieren Sie Text-to-SQL mit RAG, um Datenbanken in natuerlicher Sprache abzufragen. Ansatzvergleich, Spider-Benchmarks, Sicherheit und vollstaendiger Code.

SQL RAG: Datenbanken mit natuerlicher Sprache abfragen

80% der Unternehmensdaten befinden sich in relationalen Datenbanken. Dennoch ignorieren die meisten RAG-Systeme strukturierte Daten voellig und beschraenken sich auf Textdokumente. SQL RAG ermoeglicht es Ihren Nutzern, Fragen wie "Wer ist unser bester Kunde in diesem Quartal?" zu stellen und eine praezise Antwort direkt aus Ihrer Datenbank zu erhalten, ohne eine einzige Zeile SQL zu schreiben.

TL;DR

  • Text-to-SQL wandelt natuerlichsprachliche Fragen in ausfuehrbare SQL-Abfragen um
  • Drei Ansaetze: Direkte SQL-Generierung, RAG-augmentiertes SQL, Hybrid (SQL + Dokumente)
  • Spider-Benchmarks: Die besten Modelle erreichen 85%+ Ausfuehrungsgenauigkeit
  • Kritische Sicherheit: SQL-Injection, Zugriff auf sensible Daten, obligatorische Validierung
  • Tools: LangChain SQL Agents, LlamaIndex NLSQLTableQueryEngine, Vanna.AI
  • Anwendungsfaelle: Konversationale Analytik, automatisiertes Reporting, E-Commerce-Support

Warum SQL RAG?

Datenbanken enthalten die wertvollsten und aktuellsten Unternehmensdaten. Aber sie sind fuer Nicht-Techniker unzugaenglich.

DatenquelleKlassisches RAGSQL RAGSQL-Vorteil
PDF-DokumenteAusgezeichnetN/A-
FAQ/ArtikelAusgezeichnetN/A-
Verkaeufe/BestellungenUnmoeglichAusgezeichnetEchtzeitdaten
LagerbestandUnmoeglichAusgezeichnetImmer aktuell
Analytik/MetrikenUnmoeglichAusgezeichnetPraezise Berechnungen
KundendatenUnmoeglichAusgezeichnetPersonalisierung

Fragen, die nur SQL beantworten kann

"Wie hoch war der Umsatz letzten Monat?"
→ SELECT SUM(amount) FROM orders WHERE date >= '2026-02-01'

"Welche Produkte sind nicht auf Lager?"
→ SELECT name FROM products WHERE stock = 0

"Wer sind unsere Top-10-Kunden nach Umsatz?"
→ SELECT customer_name, SUM(amount) as total
  FROM orders GROUP BY customer_name
  ORDER BY total DESC LIMIT 10

"Wie viele Support-Tickets sind seit ueber 48 Stunden offen?"
→ SELECT COUNT(*) FROM tickets
  WHERE status = 'open'
  AND created_at < NOW() - INTERVAL '48 hours'

Die drei Ansaetze

Ansatz 1: Direkte SQL-Generierung durch LLM

Das LLM erhaelt das Datenbankschema und generiert direkt die SQL-Abfrage.

DEVELOPERpython
from langchain_openai import ChatOpenAI from langchain_community.utilities import SQLDatabase # Datenbankverbindung db = SQLDatabase.from_uri( "postgresql://user:password@localhost:5432/mydb", include_tables=["orders", "customers", "products"], sample_rows_in_table_info=3 # Beispieldaten ) llm = ChatOpenAI(model="gpt-4o", temperature=0) def generate_sql(question: str) -> str: """Generiert eine SQL-Abfrage aus einer Frage.""" schema = db.get_table_info() prompt = f"""Du bist ein PostgreSQL-SQL-Experte. Generiere eine SQL-Abfrage zur Beantwortung der Frage. Datenbankschema: {schema} Regeln: - Verwende NUR Tabellen und Spalten aus dem Schema - Gib gueltiges PostgreSQL-SQL zurueck - Begrenze Ergebnisse auf maximal 50 Zeilen - Verwende NIEMALS DROP, DELETE, UPDATE, INSERT, ALTER Frage: {question} SQL-Abfrage:""" response = llm.invoke(prompt) return response.content.strip().strip("```sql").strip("```")

Ansatz 2: RAG-Augmentiertes SQL

Reichert den LLM-Kontext mit aehnlichen Abfragebeispielen (Few-Shot) und Schema-Dokumentation an.

DEVELOPERpython
from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import FAISS from langchain_core.documents import Document # SQL-Beispiel-Wissensbasis sql_examples = [ Document( page_content="Frage: Umsatz des letzten Monats\n" "SQL: SELECT SUM(amount) FROM orders " "WHERE date >= date_trunc('month', CURRENT_DATE - INTERVAL '1 month') " "AND date < date_trunc('month', CURRENT_DATE)", metadata={"category": "revenue", "tables": "orders"} ), Document( page_content="Frage: Top 10 Kunden nach Umsatz\n" "SQL: SELECT c.name, SUM(o.amount) as total " "FROM orders o JOIN customers c ON o.customer_id = c.id " "GROUP BY c.name ORDER BY total DESC LIMIT 10", metadata={"category": "customers", "tables": "orders,customers"} ), Document( page_content="Frage: Nicht auf Lager befindliche Produkte\n" "SQL: SELECT name, sku FROM products WHERE stock = 0 " "ORDER BY name", metadata={"category": "inventory", "tables": "products"} ), ] embeddings = OpenAIEmbeddings(model="text-embedding-3-small") example_store = FAISS.from_documents(sql_examples, embeddings) class RAGAugmentedSQL: def __init__(self, db, llm, example_store): self.db = db self.llm = llm self.retriever = example_store.as_retriever( search_kwargs={"k": 3} ) def generate(self, question: str) -> str: # 1. Aehnliche Beispiele abrufen examples = self.retriever.invoke(question) examples_text = "\n\n".join([ doc.page_content for doc in examples ]) # 2. Mit Few-Shot generieren schema = self.db.get_table_info() prompt = f"""Du bist ein PostgreSQL-SQL-Experte. Schema: {schema} Aehnliche Abfragebeispiele: {examples_text} Generiere die SQL-Abfrage fuer diese Frage. Frage: {question} SQL-Abfrage:""" response = self.llm.invoke(prompt) return response.content.strip().strip("```sql").strip("```")

Ansatz 3: Hybrid (SQL + Dokumente)

Kombiniert SQL-Daten und Textdokumente fuer angereicherte Antworten.

DEVELOPERpython
class HybridSQLDocumentRAG: """Hybrides RAG, das SQL und Dokumente kombiniert.""" def __init__(self, sql_chain, doc_retriever, llm): self.sql_chain = sql_chain self.doc_retriever = doc_retriever self.llm = llm async def query(self, question: str) -> str: # 1. Frage klassifizieren needs_sql = await self._needs_sql(question) needs_docs = await self._needs_docs(question) context_parts = [] # 2. SQL-Daten abrufen falls noetig if needs_sql: sql_result = await self.sql_chain.arun(question) context_parts.append(f"Datenbankdaten:\n{sql_result}") # 3. Dokumente abrufen falls noetig if needs_docs: docs = await self.doc_retriever.aget_relevant_documents(question) doc_context = "\n".join([d.page_content for d in docs]) context_parts.append(f"Dokumentation:\n{doc_context}") # 4. Kombinierte Antwort generieren context = "\n\n---\n\n".join(context_parts) prompt = f"""Beantworte die Frage durch Kombination von Datenbankdaten und Dokumentation. {context} Frage: {question} Antwort (in natuerlicher Sprache, nicht SQL):""" return (await self.llm.ainvoke(prompt)).content

Vergleich der Ansaetze

KriteriumDirektes SQLRAG-Aug. SQLHybrid
SQL-Genauigkeit70-75%80-85%80-85%
Faktische FragenAusgezeichnetAusgezeichnetAusgezeichnet
Konzeptuelle FragenUnmoeglichUnmoeglichGut
Einrichtungszeit30 Min2-4h4-8h
WartungNiedrigMittelHoch
Kosten pro Abfrage$0,005$0,008$0,012
Latenz~2s~3s~4s
SicherheitKritischKritischKritisch

Benchmarks auf dem Spider-Datensatz

Der Spider-Datensatz ist der Standard-Benchmark fuer Text-to-SQL.

Modell / AnsatzAusfuehrungsgenauigkeitExakte SQL-Genauigkeit
GPT-4o (Zero-Shot)72,3%67,8%
GPT-4o (Few-Shot, 5 Beispiele)79,1%74,5%
GPT-4o + RAG-Beispiele83,4%78,9%
Claude 3.5 Sonnet (Few-Shot)80,7%76,2%
DIN-SQL + GPT-485,3%81,1%
DAIL-SQL + GPT-486,6%82,4%
Feinabgestimmter Spezialist88,2%84,7%

Vollstaendige Implementierung mit LangChain

DEVELOPERpython
from langchain_community.utilities import SQLDatabase from langchain_community.agent_toolkits import SQLDatabaseToolkit from langchain_openai import ChatOpenAI from langchain.agents import create_sql_agent from langchain.agents.agent_types import AgentType # 1. Datenbank-Setup db = SQLDatabase.from_uri( "postgresql://user:password@localhost:5432/ecommerce", include_tables=[ "orders", "order_items", "customers", "products", "categories" ], sample_rows_in_table_info=3 ) # 2. LLM llm = ChatOpenAI(model="gpt-4o", temperature=0) # 3. SQL-Toolkit toolkit = SQLDatabaseToolkit(db=db, llm=llm) # 4. SQL-Agent agent = create_sql_agent( llm=llm, toolkit=toolkit, agent_type=AgentType.OPENAI_FUNCTIONS, verbose=True, max_iterations=10, handle_parsing_errors=True, prefix="""Du bist ein Agent, der mit einer SQL-Datenbank interagiert. Du musst Fragen in natuerlicher Sprache beantworten. SICHERHEITSREGELN: - Fuehre NIEMALS Abfragen aus, die die Datenbank aendern (INSERT, UPDATE, DELETE, DROP) - Begrenze Ergebnisse immer (LIMIT 50 max) - Verrate NIEMALS Passwoerter oder sensible Daten - Bei mehrdeutigen Fragen, bitte um Klaerung """ )

Sicherheit: Der kritische Teil

SQL RAG legt Ihre Datenbank direkt offen. Sicherheit ist nicht optional.

1. Abfragevalidierung

DEVELOPERpython
import re from typing import Tuple class SQLValidator: """Validiert generierte SQL-Abfragen vor der Ausfuehrung.""" FORBIDDEN_KEYWORDS = [ "DROP", "DELETE", "UPDATE", "INSERT", "ALTER", "TRUNCATE", "CREATE", "GRANT", "REVOKE", "EXEC", "EXECUTE", "xp_", "sp_", "--", "/*", "*/", "UNION ALL" ] MAX_RESULT_LIMIT = 100 def validate(self, sql: str) -> Tuple[bool, str]: """Validiert eine SQL-Abfrage. Gibt (is_valid, reason) zurueck.""" sql_upper = sql.upper().strip() if not sql_upper.startswith("SELECT"): return False, "Nur SELECT-Abfragen sind erlaubt" for keyword in self.FORBIDDEN_KEYWORDS: if keyword.upper() in sql_upper: return False, f"Verbotenes Schluesselwort erkannt: {keyword}" if "LIMIT" not in sql_upper: return False, "Die Abfrage muss ein LIMIT enthalten" limit_match = re.search(r"LIMIT\s+(\d+)", sql_upper) if limit_match: limit_val = int(limit_match.group(1)) if limit_val > self.MAX_RESULT_LIMIT: return False, f"LIMIT zu hoch ({limit_val} > {self.MAX_RESULT_LIMIT})" if sql_upper.count("SELECT") > 3: return False, "Zu viele verschachtelte Unterabfragen" return True, "Abfrage validiert"

2. Nur-Lese-Datenbankbenutzer

DEVELOPERsql
-- Nur-Lese-Benutzer erstellen CREATE USER rag_readonly WITH PASSWORD 'starkes_passwort'; GRANT CONNECT ON DATABASE ecommerce TO rag_readonly; GRANT USAGE ON SCHEMA public TO rag_readonly; GRANT SELECT ON ALL TABLES IN SCHEMA public TO rag_readonly; -- Sensible Tabellen ausschliessen REVOKE SELECT ON users FROM rag_readonly; REVOKE SELECT ON payment_methods FROM rag_readonly; REVOKE SELECT ON api_keys FROM rag_readonly;

3. Sensible Daten maskieren

DEVELOPERpython
class DataMasker: """Maskiert sensible Daten in Ergebnissen.""" PATTERNS = { "email": (r"[\w.-]+@[\w.-]+\.\w+", "***@***.***"), "phone": (r"\+?\d{10,15}", "**********"), "iban": (r"[A-Z]{2}\d{2}[A-Z0-9]{11,30}", "****"), "card": (r"\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}", "****-****-****-****"), } def mask(self, text: str) -> str: for pattern_name, (pattern, replacement) in self.PATTERNS.items(): text = re.sub(pattern, replacement, text) return text

Leistungsoptimierung

TechnikAuswirkungKomplexitaet
Cache identischer SQL-Abfragen-60% LatenzNiedrig
Index auf haeufig gefilterte Spalten-70% AusfuehrungszeitNiedrig
Materialisierte Sichten fuer haeufige Aggregationen-80% AusfuehrungszeitMittel
Few-Shot-Beispiele via RAG+15% SQL-GenauigkeitMittel
Annotiertes Schema (Spaltenbeschreibungen)+10% SQL-GenauigkeitNiedrig
Feinabstimmung des Modells auf Ihr Schema+20% SQL-GenauigkeitHoch

FAQ

Ist Text-to-SQL zuverlaessig fuer die Produktion?

Mit den richtigen Schutzmechanismen, ja. Aktuelle Modelle erreichen 80-85% Genauigkeit mit Few-Shot. Die 15-20% Fehler betreffen hauptsaechlich komplexe Abfragen mit mehreren Joins. In der Produktion fuegen Sie immer Abfragevalidierung, Nur-Lese-Ausfuehrung und einen Feedback-Mechanismus hinzu, um wiederkehrende Fehler zu korrigieren.

Wie geht man mit Schemas mit Hunderten von Tabellen um?

Setzen Sie nie alle Tabellen dem LLM aus. Verwenden Sie einen Schema-Retriever, der die 3-5 relevanten Tabellen fuer jede Frage auswaehlt. LlamaIndex SQLTableRetrieverQueryEngine tut genau das. Annotieren Sie Ihre Tabellen mit klaren Beschreibungen, um dem Retriever zu helfen.

Kann SQL RAG komplexe analytische Abfragen verarbeiten (GROUP BY, HAVING, Unterabfragen)?

GPT-4o und Claude verarbeiten einfache GROUP BY und ORDER BY gut. Korrelierte Unterabfragen und Fensterfunktionen bleiben herausfordernd. Fuer komplexe Faelle erstellen Sie materialisierte Sichten, die Abfragen vereinfachen, oder verwenden domaenenspezifische Few-Shot-Beispiele.

Wie schuetzt man sensible Daten?

Dreifacher Schutz: (1) Nur-Lese-Datenbankbenutzer ohne Zugriff auf sensible Tabellen, (2) SQL-Abfragevalidierung vor Ausfuehrung (kein UNION, keine verbotenen Tabellen), (3) Maskierung sensibler Daten in Ergebnissen (E-Mails, Telefonnummern, IBANs). Vertrauen Sie dem LLM nie allein bei der Einhaltung von Vertraulichkeitsanweisungen.

Kann man SQL RAG und Dokument-RAG im selben Chatbot kombinieren?

Ja, und es wird sogar empfohlen. Der hybride Ansatz ermoeglicht es, Fragen wie "Wie hoch ist unser Monatsumsatz und wie koennen wir ihn verbessern?" zu beantworten, indem SQL-Daten (tatsaechlicher Umsatz) und Dokumentation (Verbesserungsstrategien) kombiniert werden.

Fazit

SQL RAG eroeffnet eine Welt von Moeglichkeiten, indem es Datenbanken in natuerlicher Sprache zugaenglich macht. Es ist das fehlende Stueck fuer Unternehmen, deren wertvollste Daten in PostgreSQL, MySQL oder SQL Server leben.

Wichtige Erkenntnisse:

  1. Der RAG-augmentierte Ansatz (Few-Shot + aehnliche Beispiele) bietet das beste Verhaeltnis von Genauigkeit zu Aufwand
  2. Sicherheit ist nicht verhandelbar: Nur-Lese, Validierung, Maskierung
  3. Der hybride Ansatz (SQL + Dokumente) deckt alle Fragetypen ab
  4. Das annotierte Schema ist der wichtigste Faktor fuer die Genauigkeit
  5. Materialisierte Sichten vereinfachen komplexe Abfragen

Ailog ermoeglicht es Ihnen, Ihre Datenbanken mit Ihren Chatbots zu verbinden fuer praezise Echtzeit-Antworten. Testen Sie es kostenlos und geben Sie Ihren Nutzern die Moeglichkeit, Ihre Daten abzufragen, ohne SQL zu kennen.

Ressourcen

Tags

RAGSQLText-to-SQLDatenbankenLangChainLlamaIndexNL2SQL

Verwandte Artikel

Ailog Assistant

Ici pour vous aider

Salut ! Pose-moi des questions sur Ailog et comment intégrer votre RAG dans vos projets !