Introduction

Natural-language analytics promises a simple idea: “ask” your data and get an answer. In practice, letting a large language model (LLM) talk to a database is risky: schemas are messy, permissions vary by tenant, joins are brittle, and a single unbounded query can melt your warehouse. The way forward is to treat Text-to-SQL as an operational system, not a demo. That system constrains what the model can do, verifies each step, and leaves a trail you can replay. This article lays out a production pattern for LLMs with SQL—covering architecture, safety, evaluation, and a real deployment—so teams can move from pretty prototypes to governed self-serve analytics.

Why naïve Text-to-SQL fails

LLMs are great at language, not at your organization’s semantics. They hallucinate table names, misread dimensions, over-aggregate, or ignore row-level policies. Even when the SQL runs, it may be expensive, stale, or wrong: cross-joining huge fact tables, skipping required filters (e.g., tenant_id), or querying deprecated sources. Without guardrails, you trade analyst bottlenecks for incident tickets.

An operating model that works

Successful setups split responsibility:

Think “propose → validate → compile → execute → verify,” with receipts at each step.

Architecture and context: what the model must know (and not know)

Safety and cost controls (non-negotiable)

How the prompts should change (less “magic,” more contracts)

Ditch “write a SQL query that…”; adopt a contract:

Query plan (model output)

Compiler maps this plan to SQL templates you control. This keeps reasoning short and checkable, and it makes evaluation deterministic.

Evaluation you can trust

Observability and receipts

Every request emits a trace capturing: catalog version, plan, validator outcomes, compiled SQL hash, EXPLAIN summary, execution stats (rows, bytes, time), sample of results, and lineage (tables/columns). Attach the query_id so analysts can reproduce in the warehouse UI. This makes audits—and debugging—fast.

Real-world deployment: self-serve revenue analytics

Context.
A SaaS company wanted product managers and CSMs to ask questions like “weekly expansions by plan in EMEA” without waiting on data engineering. The schema spanned billing, product events, and CRM, with strict tenant and region controls.

Design.

Outcomes (six weeks).

Implementation starter (you can adapt today)

Bundle (YAML)

bundle_id: "text2sql.v2"
purpose: "Answer analytic questions from governed views; enforce tenant/region/time filters."
allowlist:
  views: ["mrr_daily","fct_product_events","dim_account","fct_billing"]
mandatory_predicates:
  - name: tenant
    template: "tenant_id = :tenant_id"
  - name: time
    template: "date BETWEEN :start AND :end"
cost_caps:
  bytes_scanned_mb: 512
  est_rows: 5_000_000
validators:
  - no_select_star
  - require_partition_filter_on("mrr_daily","date")
  - block_cross_join
compiler:
  template_dir: "sql_templates/"
monitoring:
  log: ["plan","explain_summary","rows","bytes","duration_ms","query_id","lineage"]

Plan (model output → compiled)

{
  "intent": "Weekly MRR expansion by plan for EMEA in the last 8 weeks",
  "entities": ["mrr_daily","dim_account"],
  "join_keys": [{"left":"mrr_daily.account_id","right":"dim_account.account_id"}],
  "filters": {"region":["EMEA"], "date_range":"last_8_weeks"},
  "metrics": [{"name":"expansion_mrr","agg":"SUM","expr":"mrr_delta","where":"mrr_delta > 0"}],
  "group_by": ["week","plan_name"],
  "expected_shape": [{"col":"week","type":"date"},{"col":"plan_name","type":"string"},{"col":"expansion_mrr","type":"decimal"}],
  "checks": ["expansion_mrr >= 0"]
}

Compiler renders a parameterized SQL from trusted templates; runtime executes; receipts are logged for replay.

Practical tips & pitfalls

Conclusion

LLMs can make data genuinely self-serve—if you design the system around contracts, catalogs, and controls. Have the model propose a plan, not raw SQL. Validate against policy, compile with templates, execute in a sandbox, and ship every answer with receipts and lineage. Do this, and “ask the data” stops being a risky parlor trick and becomes a reliable, auditable capability your product and business teams can trust.