Raw text-to-SQL fails on real schemas — cryptic columns, tribal join paths, ambiguous business terms. Deployable text-to-SQL is an engineered system: curated semantic views, a fenced read-only surface, and an execution-accuracy eval that gates launch. This playbook builds all three, with accuracy measured before business users ever arrive.
Tier: Advanced · Workflow area: Governed Data Access · ~45 minutes
"Let people query in plain English" is now a stakeholder demand, and the naive version — an LLM pointed at 400 raw tables — produces confident wrong answers that erode trust in the data team. The DBA's contribution is the part no vendor demo shows: shrinking the surface to curated views the model can actually reason about, fencing the blast radius, and refusing to launch below a measured accuracy bar.
Curate semantic views — the accuracy lever nobody wants to hear about. Pick one subject area and build 3–6 views that pre-join tribal knowledge, rename cryptic columns, and pre-apply business rules:
CREATE VIEW rpt.sales_orders AS
SELECT o.id AS order_id,
o.ordered_at AS order_date,
c.region AS customer_region,
c.segment AS customer_segment,
ol.amount AS line_amount,
o.status AS order_status -- 'open','shipped','refunded'
FROM orders o
JOIN customers c ON c.id = o.customer_id
JOIN order_lines ol ON ol.order_id = o.id
WHERE NOT c.is_test;
Comment every view and column (pb-27 output goes here). The model will only ever see these views — raw tables are out of scope by design.
Fence the execution surface on a replica. A dedicated role, on a read replica, with hard limits:
CREATE ROLE nl_query LOGIN PASSWORD '...' ;
GRANT USAGE ON SCHEMA rpt TO nl_query;
GRANT SELECT ON ALL TABLES IN SCHEMA rpt TO nl_query;
ALTER ROLE nl_query SET statement_timeout = '15s';
ALTER ROLE nl_query SET work_mem = '64MB';
ALTER ROLE nl_query SET default_transaction_read_only = on;
Row caps live in the harness (append LIMIT 1000 if absent), and every executed statement is logged with the question that produced it. The blast radius is now: a slow read on a replica, killed at 15 seconds.
Write the system prompt as a contract, not a vibe.
You translate business questions into PostgreSQL SELECTs over ONLY
these views (DDL + column comments below). Rules:
- SELECT only; single statement; no DDL/DML/system functions.
- Use only listed views and columns. Never invent names.
- If the question is ambiguous or unanswerable from these views,
return REFUSE: <one-line reason> instead of SQL.
- Prefer explicit date ranges; 'last quarter' = last full calendar
quarter.
[paste view DDL with comments]
The REFUSE path is a feature: an honest "cannot answer" beats plausible wrong SQL every time, and you will score refusals in the eval.
Build the eval set before launch — 25 questions with expected results. Source them from real analyst requests: 15 straightforward, 5 requiring business-rule knowledge ("net of refunds"), 5 deliberately ambiguous or out-of-scope (correct answer: REFUSE). For each, store the expected result set or aggregate from a hand-written query. Score by execution accuracy — does the generated SQL's result match the expected result — not by whether the SQL text looks right.
Measure, curate, measure again. Run the eval through the full pipeline. Typical honest first pass: 17/25 correct, 3/5 ambiguous correctly refused. Read every failure; most trace to a missing column comment, an uncaptured business rule, or a view gap — fix the views and comments, not the prompt, and rerun. A worthwhile result: pre-curation 68% → post-curation 88%, refusal precision 5/5. Set the launch bar in writing (say, ≥85% with zero confident-wrong on the ambiguous set) and hold it.
Launch narrow, display the SQL, keep scoring. Roll out to a pilot group, one subject area. Always show the generated SQL and the row count alongside results — sunlight keeps trust calibrated. Log every question; misses become next month's eval additions and view fixes. Expansion to a second subject area repeats this recipe, not just the prompt.
sql_only first), object lists as your fence, and your own eval set run before enablement; Select AI's generated-SQL visibility is your display-the-SQL step.The question log is a sensitive artifact — users type customer names, deal details and personnel matters into search boxes. Treat NL query logs with the same access controls as the data itself, and make sure the LLM endpoint behind the feature is tenant-approved with zero-retention terms before the first real question flows.
Text-to-SQL becomes deployable exactly when the DBA does the unglamorous parts: curated views that carry the tribal knowledge, a fenced read-only surface, and an execution-accuracy eval with a pre-committed bar. The model is the least important component — and the accuracy jump from view curation proves it.