Your NL2SQL Project Failed Because the Schema Was Real
Why enterprise NL2SQL fails due to undocumented schema complexity — not model limitations — and how your workload holds the solution.
Your NL2SQL Project Failed Because the Schema Was Real
Why enterprise NL2SQL fails due to undocumented schema complexity — not model limitations — and how your workload holds the solution.

Your NL2SQL demo worked great. Fifteen clean tables, foreign keys everywhere, column names that actually made sense. Then someone pointed it at the real database — 800 tables, no FK constraints, cryptic names, years of undocumented customization. SchemaRAG fixes the part everyone else ignores: the schema itself.

While Standard RAG guesses and fails with an ORA-00904 error, SchemaRAG uses workload annotations to locate the undocumented STU_FA_XREF bridge table and generate the correct SQL.
Key Takeaways
- The Context Gap: Standard RAG relies on semantic table names and clean relationships. Enterprise NL2SQL stalls because LLMs lack the structural context of your customized, real-world systems — not because the model is bad at writing SQL.
- Your Workload Already Has the Answers: Every JOIN ever executed is evidence. SchemaRAG mines your Oracle SQL Tuning Set to discover which tables actually belong together.
- Graph Detection Finds What RAG Misses: Louvain community detection surfaces hub tables, bridge tables, and multi-hop join paths that semantic similarity search will never find.
- DBA-Owned, LLM-Agnostic: The annotation layer lives in Oracle, governed by your DBA. Swap models anytime — the correctness follows the annotations, not the LLM.
Every NL2SQL demo looks great on a clean database.
Fifteen tables. Foreign keys everywhere. Column names like customer_name and order_total. The LLM nails it. The audience applauds.
Then someone asks: “Will this work on our database?”
The room goes quiet.
The Problem Is the Schema, Not the Model
There’s an uncomfortable gap between NL2SQL benchmarks and enterprise reality. Academic benchmark schemas are clean by design — well-named tables, enforced relationships, consistent conventions. Real enterprise databases are the opposite. They accumulate complexity over years of customization, module additions, and implementation-specific decisions that no public training data reflects.
What that looks like in practice:
- No foreign keys. In many large enterprise systems, referential integrity was enforced in application code, not the database. The schema looks structurally flat. There is no join graph for a tool to read.
- Opaque naming. Module-prefixed table names, cryptic abbreviations, work tables built by contractors who left years ago. The names carry no semantic signal.
- Undocumented bridge tables. The tables that make multi-domain joins possible often have the least descriptive names and zero documentation. They are essential and invisible.
- Years of customization. Implementation-specific extensions, added tables, repurposed columns — none of it in any public training corpus.
Feed a large language model a thousand-table DDL with no FK relationships and ambiguous column names, and you get SQL that sounds confident and is quietly wrong. Not an error — just zero rows, or worse, the wrong rows with no indication anything failed.
This is not a model quality problem. It’s a context problem. LLMs are not poor at SQL. They are poor at guessing how your database is structured — especially the parts that were never published anywhere.
What Standard RAG Gets Wrong
The typical approach: embed your DDL, retrieve the tables that semantically match the user’s question, hand them to the LLM, generate SQL.
It works when the table doing the work has a name that matches the question. It breaks — quietly, confidently — in the situations that come up constantly in real enterprise schemas:
The join graph doesn’t exist. When FK constraints were never declared, or were defined in application code instead of the database, the DDL has no structural evidence that any two tables connect. The LLM has nothing to follow.
The critical table has an opaque name. A bridge table — a connector between domains that must be included for the join to work — often has the least descriptive name in the schema. Standard similarity search finds tables that sound like the question. It has no mechanism for finding tables that are structurally necessary but semantically invisible.
The path is three hops, not one. When the correct answer requires chaining through intermediate tables, one wrong guess breaks the entire query. The LLM doesn’t know which intermediate tables exist, let alone which ones belong in this particular chain.
The business question shares no words with the table name. “Which items need reordering” and the table that answers it may have nothing in common linguistically. Name-based retrieval has no path to it at all.
The result is always the same: syntactically valid SQL that returns zero rows, or worse, the wrong rows with no indication anything failed. Your team spends weeks writing prompt rules. Things improve slightly. A new query type breaks everything again.
This is the gap SchemaRAG was built to close.
The Insight: Your Workload Already Knows
Your database has been running queries for years. Every JOIN that was ever executed is evidence — evidence of which tables belong together, which bridge tables are essential, which multi-hop paths actually work. That evidence is sitting in your Oracle SQL Tuning Set (STS) right now, unused. For those unfamiliar, an STS is a built-in Oracle database feature that automatically captures a historical record of executed SQL statements, execution plans, and performance statistics. Because DBAs routinely use it for performance monitoring, this rich workload history is already being collected in the background of most enterprise Oracle deployments.
SchemaRAG reads your production workload and turns it into something an LLM can use.
We’ve implemented this as an open-source pipeline called the SchemaRAG Demo, built on a synthetic 70-table university schema designed to mirror the structural complexity of real enterprise deployments — opaque table names, no foreign keys, undocumented bridge tables, cross-domain joins. The university schema is the concrete anchor throughout this post; the repo link is in the ‘Try It’ section if you want to run it yourself. The pipeline works in six steps:
1. Mine the workload, not the DDL Parse the SQL Tuning Set. Count every JOIN co-occurrence. Compute a Jaccard-based affinity score for each table pair — weighted by both the number of distinct query templates that join two tables and their total execution counts. Tables joined constantly by many different queries get HIGH affinity. Tables rarely joined get LOW. Tables never joined get excluded — which is also a meaningful signal.
2. Build a schema graph Translate the affinity matrix into a weighted graph. Nodes are tables. Edges are proven join relationships, weighted by frequency.
3. Run Louvain community detection Louvain is a widely used network analysis algorithm that automatically discovers distinct clusters of frequently connected items within a larger graph. Without being told anything about business domains, the algorithm discovers which tables cluster together naturally based on their join frequencies. In the university demo schema, it finds ten communities — RegistrarCore, FinancialAid, Bursar, Compliance, and more — purely from join behavior, with no domain knowledge supplied.
4. Identify hubs and bridges Hub tables sit at the intersection of multiple communities. Bridge tables connect domains and make cross-domain queries possible. These are exactly the tables standard RAG consistently misses.
5. Annotate All of this graph-derived knowledge gets encoded into structured bracket-triple annotations appended to each table’s metadata document:
[STU_FA_XREF BRIDGES RegistrarCore:FinancialAid]
[ENRL_REC IS_HUB degree:5.0]
[ENRL_REC JOINS_PATH STU_MST→ENRL_REC→ACAD_EXCEPTION_WRK]
[STU_FA_XREF MEDIUM_AFFINITY FINANCIAL_AID_APPLICATION:0.21]
6. Store and retrieve
Everything lands in a single Oracle table — UNIV_EMBEDDINGS — one annotated document per table. At query time, a lightweight retrieval step finds the relevant tables including their bridge and hub neighbors. The LLM receives focused, annotation-enriched context and generates SQL that reflects how the schema actually behaves.
No model retraining. No schema rebuilding. No data science team. The database you already have is doing the work.
The Annotation Types Do Specific Jobs
Each annotation answers a question the LLM would otherwise have to guess at:
**IN_COMMUNITY RegistrarCore** — This table belongs to the registrar domain; deprioritize it for financial aid queries.
**IS_HUB degree:5.0** — This table connects five communities. Include it in any cross-domain query.
**BRIDGES RegistrarCore:FinancialAid** — This table connects these two specific domains. Don't skip it.
**HIGH_AFFINITY STU_MST:0.71** — These two tables are joined constantly in production. They belong together.
**JOINS_PATH STU_MST→ENRL_REC→ACAD_EXCEPTION_WRK** — This three-hop path has been executed in production. Follow it.
The format matters too. While plain-text metadata (“Table A is used with Table B for financial queries”) forces the LLM to parse natural language and infer the exact relationship, bracket-triple [SUBJECT PREDICATE OBJECT] notation delivers deterministic, unambiguous facts. This syntax maps directly onto knowledge graph structures (like RDF triples) that appear extensively in LLM training data. Because the model has seen millions of examples of this exact structural pattern, it reasons over these hard links efficiently and without being coached, completely bypassing the ambiguity and hallucination risks of free-text descriptions.
The practical outcome of feeding the LLM these structured, graph-like facts is immediate: you see drastically fewer missing joins, a near-elimination of hallucinated multi-hop join paths, and significantly higher overall query execution success rates across complex enterprise schemas.
Where This Gets Interesting: Schemas With No Semantic Signal at All
The university demo is a useful proof of concept — realistic complexity, reproducible, runnable in under an hour on Oracle Autonomous AI Database. But it still has table names with words in them.
Some enterprise schemas don’t have that.
Consider JD Edwards. Every table name is an alphanumeric code: F0101, F4311, F43121. Every column name is a 6–8 character abbreviation: ABAN8, ABALKY, DOCO. There is no semantic signal anywhere in the DDL. An LLM reading a JDE schema cannot reason about what any table does, what any column holds, or which tables belong together — because the names carry zero information.
On a JDE schema, workload-derived annotations aren’t an improvement over DDL-alone retrieval. They’re the only viable path. The SQL Tuning Set knows that F4311 and F43121 are joined thousands of times per day. The DDL cannot tell you that.
PeopleSoft follows a similar pattern — PS_-prefixed tables, cryptic field names, cross-module joins that only make sense if you understand the underlying business process. Healthcare systems like those from Cerner carry heavy per-deployment customization, undocumented bridge tables, and EHR-specific extensions that look nothing like any public schema. Oracle EBS installations often carry significant customer-specific extension tables — built over years to handle business logic the base product didn’t cover — that exist nowhere in any documentation and whose join targets are inferrable only from the workload.
These aren’t edge cases. They’re a description of most large enterprise Oracle installations in production today.
This Is Not for Everyone
SchemaRAG is not for small, well-documented databases. If your schema has fewer than 100 tables, meaningful column names, enforced foreign keys, and solid documentation — existing NL2SQL tooling will serve you well.
This was designed for the opposite:
- Databases with years or decades of use and accumulation
- Systems where institutional knowledge lives in the heads of a few people close to retirement
- Implementations with significant customization that was never properly documented
- Schemas where half the tables have uncertain origins, but the workload knows exactly which ones matter
If you read that and thought “that’s my database” — keep reading.
The Architecture Is DBA-Owned By Design
One of SchemaRAG’s most important properties is that the annotation layer is a DBA-controlled asset, completely decoupled from whatever LLM your organization uses.
The DBA curates UNIV_EMBEDDINGS. Applications query a governed Oracle ORDS endpoint that returns annotation context for any NL question. The LLM — Claude, GPT-4, Llama, Gemini, an on-premise model — reads the annotations and generates correct SQL. Swap the model; the correctness follows the annotations, not the model choice.
A single Oracle installation can serve a heterogeneous user base — internal teams on one model, regulated workloads on another — all drawing from the same DBA-governed annotation layer. This centralized schema intelligence means your organization stops duplicating prompt engineering efforts across different departments. Instead of five separate application teams writing custom prompt rules to explain how to join the same undocumented tables, the database provides the correct structural context automatically, drastically reducing development overhead. The DBA never needs to know which model the user chose.
Try It
The full implementation is open source — pipeline, demo schema, 12 test scenarios, and the enterprise deployment pattern:
👉 **SchemaRAG Demo — oracle/microservices-backend**
You’ll need Oracle Autonomous AI Database, Python 3.11+, and an API key for your LLM of choice. The demo ships configured for Claude but the annotation pipeline is LLM-agnostic — swap in GPT-4, Gemini, Llama, or any model you already use by updating a single environment variable. The demo runs against a synthetic 70-table university schema with a realistic workload baked in. Full Standard RAG vs. SchemaRAG side-by-side comparison, running in under an hour, without needing your production schema. By the end of the run, success is immediately visible: you will watch the LLM correctly navigate previously hidden multi-hop relationships and bridge tables, proving a massive leap in retrieval quality and SQL accuracy over standard approaches.
Let’s Talk
If you’re running a complex Oracle database — heavily customized, poorly documented, years of accumulated join history — and you’ve wondered whether NL2SQL could realistically work on it, I’d like to find out together.
I’m particularly interested in hearing from teams whose NL2SQL projects looked promising in the demo and fell apart when they hit the real schema. That gap is exactly what this was built to close.
Reach out in the comments or find me on LinkedIn. The hard databases are the interesting ones.
Frequently Asked Questions
- We already tried standard RAG. Why does this work when standard approaches fail on complex schemas?
Standard RAG relies on semantic similarity and declared foreign keys. If your tables are named like
F0101orPS_PERSONAL_DATA(like in JD Edwards or PeopleSoft), or if constraints are enforced purely in the application layer, standard tools see a flat, meaningless schema and guess wrong. SchemaRAG ignores names and DDL structure, relying instead on your SQL Tuning Set. It finds tables that are structurally necessary (like undocumented bridge tables) based on actual production join history. - Does this require exposing our production data or fine-tuning an LLM? No on both counts. No production data leaves the database because SchemaRAG only mines the query structure (metadata) from the workload, not the rows inside them. Furthermore, no fine-tuning is required. The intelligence lives securely in your Oracle annotations, not the model, meaning you can swap freely between Claude, GPT-4, Llama, or any on-premise model out of the box.
- Our schema has hundreds of tables. What is the realistic effort to deploy and maintain this? The pipeline is designed to be incremental. You don’t have to annotate 800 tables on day one; you can run the Louvain community detection and start with the specific domain your users ask about most. Maintenance is also lightweight — you only update affected tables and re-run community detection when significant schema changes occur, rather than rebuilding from scratch.
- Who actually owns and manages this architecture once it’s built? Your DBA. The annotation layer lives in an Oracle table and is governed by your existing data access controls. Applications query it through an Oracle ORDS endpoint, ensuring the DBA maintains complete governance over what structural context the LLM is allowed to see at query time.
- What if our SQL Tuning Set is sparse or we haven’t enabled it yet? A sparse workload still beats raw DDL alone, giving the LLM significantly more architectural context to work with. If you’re starting entirely fresh, enable the SQL Tuning Set now — every query that runs from this point forward builds your annotation foundation.
- What if our business questions use terminology that doesn’t appear anywhere in the schema? SchemaRAG is built to solve the structural routing problem, but it doesn’t automatically map external business lingo. For terminology mapping, your DBA can extend the annotations over time with business glossary terms to catch edge-case phrasing as it surfaces.
메타데이터
- post_id
- eebaed6f867a
- slug
- your-nl2sql-project-failed-because-the-schema-was-real-eebaed6f867a
- url
- https://medium.com/oracledevs/your-nl2sql-project-failed-because-the-schema-was-real-eebaed6f867a
- canonical_url
- https://medium.com/oracledevs/your-nl2sql-project-failed-because-the-schema-was-real-eebaed6f867a
- author_url
- https://medium.com/@DatabaseDoug
- status
- ok
- fetched_at
- 2026-07-09 08:27:28