How to chat with your SQL database — and trust the answers
By SchemaChat (Data BI LLC) · Published 30 October 2026 · Last reviewed 30 October 2026 · 8 min read
To chat with a SQL database you connect it to a tool that turns your plain-English question into a query, runs it, and explains the result. Do it safely: give the tool a read-only database user, start with questions you already know the answer to, and check the SQL, the filters and the row count behind every number.
What “chatting with a database” actually does
- It reads your schema: table and column names, and types.
- A language model turns your question into a SQL query.
- The query runs against your database.
- The result is explained in plain language, usually with a table or chart.
The risk sits in steps 1–2: the model can pick the wrong table, add a filter you didn't ask for, or misread a column. That is why the SQL and the way the result was produced must be visible.
Three ways to do it
| Approach | Good for | Watch out for |
|---|---|---|
| Build it yourself with an open-source framework (for example Vanna) | Developers who want full control and are happy to run and secure it | You own accuracy testing, access control and upkeep |
| A SQL client with an AI assistant | People who already write SQL and want faster drafts | Usually assumes you can read and judge the SQL |
| A hosted AI analyst (SchemaChat is one) | Teams who want answers and dashboards without a data department | Check what it shows you for each answer and what it does when it can't answer |
Step 1 — Give the tool a read-only database user
Never connect any tool with the credentials your application uses. Create a user that can only read, and grant it only the schemas you want analysed.
PostgreSQL
CREATE ROLE analyst_ro LOGIN PASSWORD 'choose-a-long-random-password';
GRANT CONNECT ON DATABASE mydb TO analyst_ro;
GRANT USAGE ON SCHEMA public TO analyst_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO analyst_ro;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO analyst_ro;MySQL
CREATE USER 'analyst_ro'@'%' IDENTIFIED BY 'choose-a-long-random-password';
GRANT SELECT ON mydb.* TO 'analyst_ro'@'%';- If you can, point the tool at a read replica so analysis never competes with production traffic.
- Check where the tool sends your question text and results, and read its sub-processor list.
- Prefer encrypted connections. If the provider requires TLS (Supabase and Neon do), enable it.
- SchemaChat connects to databases reachable from the internet; a database that is only reachable inside a private network is not supported yet.
Step 2 — Start with questions you can verify
Pick five questions whose answers you already know — a total you reconcile every month, a count you can check in a SQL client. If the tool gets those wrong, stop. If it gets them right, widen out.
Step 3 — Read the answer, not just the number
In SchemaChat every answer that runs a query shows its SQL and a “How this was computed” receipt: the tables used, the filters applied, how many rows were counted, and any assumption made. Whatever tool you use, look for the same four things:
- Which tables were used.
- Which filters were applied — especially any you didn't ask for.
- How many rows were counted.
- What happens to missing values.
Five ways answers go wrong (real examples from our own testing)
We keep a test suite of questions with independently computed answers (see how we test). These are real defects it caught, all on sample data we generate ourselves:
- Integer division. On a table where two columns held whole numbers, averaging gross ÷ pieces returned 786.98 when the true mean was 787.23, because SQLite divides two integers as an integer (7 ÷ 2 = 3). Nothing errored; the number just looked plausible.
- A silent filter. For “how many tickets have a CSAT score?”-style questions, a model added an unrequested “is not empty” condition that changed the count. Receipts list every filter so it can be spotted.
- Dates as text. A column of month labels like “Jun-21” cannot be parsed by SQLite's date functions: they return NULL, so ordering by them put every row in one group and a “running total” showed the grand total on every row. The fix is to convert the label to a sortable key (2021-06) first.
- Pasted lists inside a sales table. A block of mostly-empty rows below the data made a missing-value rate read 12.2% instead of 7.4%. See the guide on messy spreadsheets.
- A made-up field. Asked to group by a column that doesn't exist, a tool should say so rather than quietly choose the nearest column.
A checklist before you trust a number
- Do I know how many rows this counted?
- Did it apply a filter I didn't ask for?
- Do the first and last rows of a running total or ranking make sense?
- Does the total reconcile to something I already trust?
- Would I put my name on this in a meeting?
Try it
You can try the SchemaChat demo on a sample database with no signup, or start a 14-day team trial and connect your own database with the read-only user above.