#ImpolyzerDataStudio© #AI #Text to SQL #ETL #RAG
POSTED , 24 GIU 2025
Dal RAG allo Schema-Guided SQL Generation: Evoluzione dei Sistemi per la Generazione Strutturata di Query e regole di import/export
Introduzione
I moderni sistemi di generazione di codice SQL stanno adottando tecniche avanzate di Intelligenza Artificiale per tradurre richieste in linguaggio naturale in query precise e ottimizzate. Tra questi, il Retrieval-Augmented Generation (RAG) è emerso come un approccio potente, combinando recupero di informazioni contestuali e generazione dinamica.
Tuttavia, in contesti specializzati come la generazione di query SQL per database strutturati (es. Firebird, PostgreSQL,Oracle,Interbase,ect), è possibile definire un’evoluzione più mirata del RAG: un Domain-constrained SQL generation framework (o alternativamente, Prompt-guided structured generation system).
Ma cosa distingue questo sistema da un RAG tradizionale? E perché è particolarmente efficace per l’interazione con database complessi?
1. RAG Classico vs. Schema-Guided SQL Generation
Retrieval-Augmented Generation (RAG)
Il RAG standard combina due fasi:
Retrieval: Recupera informazioni da una knowledge base esterna (es. documenti, articoli).
Generation: Genera una risposta basata sui dati recuperati e sul contesto della domanda.
Esempio:
Un chatbot che risponde a domande tecniche recuperando pezzi di manuali.
Schema-Guided SQL Generation (SGSG)
Nel nostro caso, il sistema specializzato opera in modo più strutturato:
Retrieval vincolato: Invece di cercare in documenti generici, accede a uno schema di database predefinito (tabelle, campi, relazioni).
Generazione guidata da regole: Produce SQL seguendo vincoli precisi (sintassi del DBMS, assenza di alias non supportati, ecc.).
Mappatura semantica: Traduce termini naturali (es. "contribuente") in campi DB (es.
H531UTENTI.IDUTENTE
).
Esempio:
Data la richiesta "totale canone permanente anno 2025 per ogni contribuente del comune H531", il sistema:
Identifica le tabelle rilevanti (
H531EREGISTRO2025
,H531UTENTI
).Applica filtri basati su
TIPOCOD = 1
(per "permanente").Genera una query con
GROUP BY IDUTENTE
eSUM(IMPORTO)
.
2. Perché è Più di un Semplicistico RAG?
Mentre il RAG tradizionale è general-purpose, il nostro sistema introduce:
a) Vincoli di Dominio Forti
Schema-aware: Non cerca informazioni in modo aperto, ma naviga una struttura DB nota.
DBMS-specific: Rispetta i limiti del DB attivo (es. Firebird 3.0+)
b) Prompt Engineering Specializzato
I "prompt" non sono domande generiche, ma includono:
Placeholder dinamici (es.
{{dynamic_codifica}}
→H531ECODIFICA2025
).Traduzione semantica ("canone" → campo
IMPORTO
).
c) Controllo Strutturato dell’Output
Non genera testo libero, ma solo SQL valido per lo schema fornito.
Evita allucinazioni (es. non inventa campi inesistenti).
3. Nomenclatura: Quale Termine Usare?
Ecco alcune definizioni alternative, con pro e contro:
Termine | Vantaggi | Svantaggi |
---|---|---|
Domain-constrained SQL generation framework | Precisa il dominio (SQL) e i vincoli. Ottimo per tecnici. | Un po’ lungo. |
Prompt-guided structured generation system | Enfatizza il ruolo del prompt. Più generale. | Non specifica il contesto SQL. |
Schema-Guided SQL Generator (SGSG) | Breve, tecnico, intuitivo. | Meno noto. |
La nostra scelta: Domain-constrained SQL generation framework cattura meglio la natura ibrida (IA + regole di dominio).
4. Caso d’Uso: Generazione di Query Complesse
Il sistema eccelle in scenari come:
Traduzione di richieste amministrative (es. "lista contribuenti morosi").
Adattamento a DB legacy (Firebird, Oracle,Interbase,SQLLite,ect) con sintassi specifiche.
Mantenimento della compliance: Query sempre allineate allo schema DB.
Esempio di output generato:
SELECT U.IDUTENTE, U.RAGIONE_SOCIALE, SUM(R.IMPORTO) AS TOTALE_CANONE_2025 FROM H531UTENTI U JOIN H531EREGISTRO2025 R ON U.IDUTENTE = R.IDUTENTE WHERE R.IDCODIFICA IN (SELECT COD FROM H531ECODIFICA2025 WHERE TIPOCOD = 1) AND R.DATAINIZIO >= '2025-01-01' AND R.DATAFINE <= '2025-12-31' GROUP BY U.IDUTENTE, U.RAGIONE_SOCIALE
Chiavi disponibili
1. Chiavi Principali
Chiave Tipo Descrizione Esempio tags
Array di stringhe Tecnologie/DBMS compatibili ["SQL", "FIREDAC", "FIREBIRD 3.0+"]
question
Stringa Domanda in linguaggio naturale da tradurre in SQL "totale canone permanente anno 2025..."
constraints
Array di stringhe Vincoli per la generazione della query ["Usa solo SQL compatibile con FIREBIRD 3.0+"]
placeholders
Oggetto (key-value) Sostituzioni dinamiche nello schema/tabelle "[[COMUNE]]CODIFICA{{ANNO}}"
→"H531ECODIFICA2025"
tables
Oggetto annidato Schema completo del database (tabelle + campi) Vedi sotto semantic_mapping
Oggetto (key-value) Mappatura termini naturali → strutture DB "canone" → "REGISTRO.IMPORTO"
2. Sottochiavi di
tables
Ogni tabella definita in
tables
deve includere:Sottochiave Tipo Descrizione fields
Array di stringhe Lista di campi della tabella keyfields
Array di stringhe Campi che compongono la chiave primaria Esempio:
"H531EREGISTRO2025": { "fields": ["IDMASTER", "ID", ...], "keyfields": ["IDMASTER", "ID"] }
3. Sottochiavi Speciali
In
placeholders
:Supporta sintassi con
[[ ]]
(obbligatori) e{{ }}
(opzionali)."[[COMUNE]]CODIFICA{{ANNO}}"
→ Se ANNO=2025, diventa"H531ECODIFICA2025"
.
In
semantic_mapping
:Può riferirsi a:
Tabelle (es.
"contribuente" → "UTENTI"
).Campi (es.
"totale" → "IMPORTO"
).Condizioni SQL (es.
"occupazione" → "IDCODIFICA IN (SELECT COD FROM ...)"
).
4. Chiavi Opzionali
Chiave Scopo aliases
Mappatura di alias per tabelle/campi (se supportati dal DBMS). custom_functions
Definizione di funzioni SQL personalizzate ammesse. Esempio con
aliases
Utile per semplificare query complesse con riferimenti a tabelle/campi lunghi:
{ "tags": ["FIREBIRD 3.0+"], "question": "elenco ragione sociale e totale canone 2025 per contribuente", "constraints": ["Usa alias solo se specificato esplicitamente"], "aliases": { "tabelle": { "H531EREGISTRO2025": "R", "H531UTENTI": "U" }, "campi": { "RAGIONE_SOCIALE": "RAG_SOC", "IMPORTO": "TOTALE" } }, "tables": { ... }, "semantic_mapping": { ... } }
SQL Generato:
SELECT U.RAGIONE_SOCIALE AS RAG_SOC, SUM(R.IMPORTO) AS TOTALE FROM H531EREGISTRO2025 R JOIN H531UTENTI U ON R.IDUTENTE = U.IDUTENTE GROUP BY U.RAGIONE_SOCIALE
Esempio con
custom_functions
Per integrare funzioni specifiche del DBMS o personalizzate:
{ "tags": ["FIREBIRD 3.0+"], "question": "differenza giorni tra data inizio e fine accertamento", "constraints": ["Usa solo funzioni dichiarate in custom_functions"], "custom_functions": { "DIFF_GIORNI": "DATEDIFF(day, ?1, ?2)", "FORMATO_DATA": "CAST(?1 AS VARCHAR(10))" }, "tables": { "H531EREGISTRO2025": { "fields": ["ID", "DATAINIZIO", "DATAFINE"], "keyfields": ["ID"] } } }
SQL Generato:
SELECT ID, DIFF_GIORNI(DATAINIZIO, DATAFINE) AS GIORNI_DIFF FROM H531EREGISTRO2025
Combinazione di
aliases
+custom_functions
{ "aliases": { "tabelle": { "H531EREGISTRO2025": "REG" } }, "custom_functions": { "IS_MOROSO": "CASE WHEN REG.PAGATO = 0 AND REG.SCADENZA < CURRENT_DATE THEN 1 ELSE 0 END" }, "question": "cerca contribuenti morosi con canone > 1000", "tables": { ... } }
SQL Generato:
SELECT REG.ID, U.RAGIONE_SOCIALE FROM H531EREGISTRO2025 REG JOIN H531UTENTI U ON REG.IDUTENTE = U.IDUTENTE WHERE IS_MOROSO(REG.ID) = 1 AND REG.IMPORTO > 1000
Note Importanti:
Compatibilità:
Le
custom_functions
devono essere supportate dal DBMS.Gli
aliases
sono opzionali e disattivabili viaconstraints
(es.["NO alias"]
).
Sicurezza:
Validare sempre le
custom_functions
per evitare SQL injection.
Parametri:
Usare
?1
,?2
... per segnaposto di parametri nelle funzioni.
5. Vincoli di Utilizzo
Obbligatorie:
tables
,question
,constraints
.
Consigliate:
semantic_mapping
(migliora l'accuratezza).placeholders
(per query dinamiche).
Ignorate se assenti:
aliases
,custom_functions
.
Esempio Completo Minimo:
{ "tags": ["SQL", "FIREBIRD"], "question": "contare i contribuenti attivi", "constraints": ["NO alias"], "tables": { "UTENTI": { "fields": ["IDUTENTE", "STATO"], "keyfields": ["IDUTENTE"] } }, "semantic_mapping": { "contribuenti": "UTENTI", "attivi": "STATO = 'ATTIVO'" } }
Output SQL Generato:
SELECT COUNT(*) FROM UTENTI WHERE STATO = 'ATTIVO'
Nel sistema che hai presentato, la chiave filters
non è inclusa tra quelle standard, ma può essere implementata in due modi, a seconda delle esigenze:
Uso Alternativo di semantic_mapping
per Filtri
Se per "filters" intendi condizioni SQL predefinite, queste possono essere inserite direttamente nel semantic_mapping
:
"semantic_mapping": { "occupazione": "REGISTRO.IDCODIFICA IN (SELECT COD FROM {{dynamic_codifica}} WHERE TIPOCOD = 1)", "attivo": "STATO = 'ATTIVO'" },
"placeholders": { "dynamic_codifica": "[[COMUNE]]CODIFICA{{ANNO}}", },
Esempio di utilizzo:
Se la domanda è "mostra i contribuenti attivi", il sistema tradurrà "attivo"
nella condizione SQL associata.
La Chiave Custom filters
Per filtri complessi, si può estender lo schema JSON:
{ "filters": { "filtro_occupazione": "TIPOCOD = 1 AND DATAFINE >= CURRENT_DATE", "filtro_moroso": "PAGATO = 0 AND SCADENZA < CURRENT_DATE" } }
Vantaggi:
Separazione netta tra mappatura semantica e condizioni riutilizzabili.
Possibilità di riferirsi ai filtri con alias (es.
"applica filtro_moroso"
).
Differenza tra filters
e constraints
constraints
: Vincoli tecnici sulla generazione SQL (es. sintassi Firebird)."constraints": ["NO WITH clause", "Usa solo JOIN espliciti"]
filters
(se aggiunta): Vincoli logici sui dati (es. filtri temporali o di stato).
Esempio Completo con filters
Personalizzata
{ "question": "totale canoni occupazione non pagati nel 2025", "constraints": ["NO alias", "Usa sintassi Firebird 3.0+"], "tables": { ... }, "semantic_mapping": { "canone": "REGISTRO.IMPORTO", "occupazione": "[[FILTRO_OCCUPAZIONE]]"
}, "filters": { "FILTRO_OCCUPAZIONE": "IDCODIFICA IN (SELECT COD FROM H531ECODIFICA2025 WHERE TIPOCOD = 1)", "FILTRO_NON_PAGATO": "ID NOT IN (SELECT IDPAGAMENTO FROM PAGAMENTI)" } }
SQL Generato:
SELECT SUM(IMPORTO) FROM H531EREGISTRO2025 WHERE IDCODIFICA IN (SELECT COD FROM H531ECODIFICA2025 WHERE TIPOCOD = 1) AND ID NOT IN (SELECT IDPAGAMENTO FROM PAGAMENTI)
Se vuoi standardizzare il sistema
Approccio Conservativo: Usa solo
semantic_mapping
per i filtri (come nell'esempio iniziale).Approccio Esteso: Aggiungi
filters
come chiave opzionale per condizioni riutilizzabili.
Raccomandazione: Se lavori in un contesto con molti filtri ricorrenti (es. "moroso", "storico", "in scadenza"), la chiave filters
migliora la manutenibilità.
Conclusioni: ImpolyzerDataStudio verso Sistemi Ibridi AI + Rules
Questo framework rappresenta un ponte tra NLP e database strutturati, superando i limiti del RAG generico:
Maggiore accuratezza: Niente allucinazioni su schemi DB.
Mantenibilità: Aggiornando lo schema, si aggiorna il generatore.
Adattabilità: Può essere esteso a altri DBMS (tutti i DB Firedac e CDATA,CSV, Access, Excel,memTable,ect).
Nessun commento:
Posta un commento