AI for SQL: how to write and optimize queries with a model

Schema, sample data and a query plan in the request to the model, an EXPLAIN check and read-only access. Working Python code and a cost estimate.

6 min · · development · API keys

An AI model for SQL programming can write queries from a description and explain why a query is slow. But without the database schema it guesses table names, and without checking it can produce a query that runs but returns the wrong thing. Worst of all is a generated UPDATE or DELETE landing in a production database. Below is a working order that reduces these risks: what to give the model, how to verify a query with EXPLAIN and how to limit access.

The pipeline "question in plain language, SQL in reply" for reporting interfaces is covered in the article generating SQL queries with AI. Here the accent is different: a developer's and analyst's daily work, optimization and safety.

AI for SQL programming: what to give the model

A request like "write SQL for the top 10 customers by revenue" with no context produces a query over invented tables. Pass four things:

  1. DBMS and version. PostgreSQL 16, MySQL 8, SQLite: date functions, window functions and LIMIT differ.
  2. Schema. CREATE TABLE with keys, constraints and indexes fits best. The model sees types and relations.
  3. A few sample rows. Two or three anonymized rows per table. They show the value format: statuses as strings, dates as text or timestamp, how amounts are stored.
  4. Table sizes. The order of magnitude of row counts. For optimization it matters more than it seems: a table with a hundred rows needs no index.

You can export the schema with your DBMS's standard tools (for example pg_dump --schema-only for PostgreSQL). Do not send real customer data: the structure and anonymized examples are enough.

Request template

bash
DBMS: PostgreSQL 16.
Schema:
CREATE TABLE orders (id bigint PRIMARY KEY, customer_id bigint NOT NULL,
  status text NOT NULL, created_at timestamptz NOT NULL, total numeric(12,2));
CREATE INDEX orders_customer_idx ON orders (customer_id);
CREATE TABLE customers (id bigint PRIMARY KEY, name text, region text);
Volume: orders about 40M rows, customers about 800K.
Statuses: 'new', 'paid', 'cancelled'.

Task: revenue by region for October 2026 from paid orders.
Requirements: SELECT only, month bounds without BETWEEN on a timestamp,
explain the logic in one paragraph after the query.

Two details save edits: list the allowed status values explicitly (otherwise the model invents completed) and ask for date bounds as >= '2026-10-01' AND < '2026-11-01'. The typical BETWEEN error on timestamp loses the last day.

Verification: EXPLAIN instead of faith

A readable-looking query may turn out to be a full scan of 40 million rows. So every generated query gets a plan step:

  • EXPLAIN shows the plan without running the query. Start with it.
  • EXPLAIN (ANALYZE, BUFFERS) in PostgreSQL really executes the query and shows actual time. For a SELECT that is safe with a timeout and on a replica, but for queries that modify data ANALYZE will execute them, so do not run those through it. Syntax and output in MySQL and other DBMSs differ; consult your own documentation.

Then a "query, plan, explanation" loop works well: you send the model the original query and plan output, ask it to explain the bottleneck and offer options (query rewrite, index), and then test each option on a copy of the database. A suggested index is created by a person through a normal migration, not by a script with a model.

Protecting the database: four barriers

The code below shows a chain of barriers for generated SELECT queries:

bash
import os
import psycopg
import sqlglot
from sqlglot import exp
from openai import OpenAI

client = OpenAI(base_url="https://api.zukko.pro/v1",
                api_key=os.environ["ZUKKO_API_KEY"])

def generate_sql(schema, task):
    resp = client.chat.completions.create(
        model="claude-sonnet-5-5",
        messages=[
            {"role": "system", "content": "You are a PostgreSQL 16 assistant. "

The barriers in order:

  1. Query parsing. sqlglot checks that there is exactly one statement and it is a SELECT, not SELECT 1; DROP TABLE .... This is a syntactic check: it will not catch function side effects, so the next barrier is mandatory.
  2. A read-only role. The connection string uses a DBMS user that has only SELECT on the needed tables. This is the main barrier: it works even if all other checks are bypassed.
  3. A read-only transaction and a timeout. The query cannot change data and will not occupy the database for long.
  4. Execution on a replica or a copy. Heavy analytical queries must not interfere with production load.

If the model was asked to fix data, that is a separate task: the model produces a script, a person reads it, runs it on a copy, wraps it in a transaction and launches it themselves.

Typical errors worth looking for

  • Duplicates after JOIN. Joining a one-to-many table multiplies rows, and SUM returns an inflated total.
  • NOT IN with NULL. The subquery returns NULL and the condition stops matching. NOT EXISTS is often what you need.
  • Functions of another DBMS. The model confuses DATE_TRUNC, STRFTIME, DATEADD if the dialect is not stated.
  • Implicit type casts that prevent index use.
  • SELECT * in production queries and sorting without LIMIT.

Check results against control numbers: run the query by hand on a small slice and compare the total. And when optimizing, compare the results of the old and new queries on a copy, for example with EXCEPT in both directions: an empty result in both directions means identical row sets.

Which model to choose

Cheap models suffice for simple queries; complex analytics with window functions and plan optimization are worth running on several. Make 15-20 of your own queries with known answers and compare. Zukko prices at October 18, 2026 ($ per 1M tokens, input / output) and strength index:

ModelPriceStrength index
deepseek-v4.1-flash0.013 / 0.03839
gpt-6.1-sol0.27 / 1.3252
claude-sonnet-5-50.4 / 248
claude-opus-5-50.8 / 458

The index does not measure SQL quality on your schema; it is a reference point. A roundup on choosing a model for code: the best AI for programming.

What it costs

Formula: (input × input price + output × output price) / 1,000,000. Assumptions: an optimization request, 3,000 schema tokens, 100 question tokens, 800 plan tokens, in total 3,900 input and 500 output tokens.

  • deepseek-v4.1-flash: 3,900 × 0.013 / 1,000,000 + 500 × 0.038 / 1,000,000 = 0.0000507 + 0.000019, about $0.00007.
  • gpt-6.1-sol: 3,900 × 0.27 / 1,000,000 + 500 × 1.32 / 1,000,000 = 0.001053 + 0.00066, about $0.0017.
  • claude-sonnet-5-5: 0.00156 + 0.001, about $0.0026.
  • claude-opus-5-5: 0.00312 + 0.002, about $0.0051.

Twenty such requests a day on claude-sonnet-5-5 come to about $0.05. The schema repeats in every request, so with frequent calls it pays to cache it: the Claude cache discount comes from the Anthropic format, details in the article prompt caching: reads and writes. Fresh prices are always in the GET https://api.zukko.pro/v1/models response.

Connecting

Create a key in the dashboard, set the base URL https://api.zukko.pro/v1 and connect following the chat completions reference. The balance is topped up from $1: SBP (the payment page adds 8.5% on top), cryptocurrency or a MasterCard.

FAQ

Can I give an AI direct access to the database?

Only with a role that has read rights on the needed tables, and preferably on a replica. Never give a model write permissions.

How does an AI for generating SQL code learn the schema?

Only from your request. Pass CREATE TABLE with indexes and sample rows, otherwise it will guess names.

Does the model really optimize queries?

It offers options based on the plan you showed it. The effect is verified on a copy of the database, and a person creates the index.

How do I check that an optimized query returns the same result?

Compare the results of the two queries on a copy with EXCEPT in both directions and check the totals.

zukko

Pay 25×
less

for Claude Code & ChatGPT

One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.

Get API keyDocumentation

api.zukko.pro · fetch_*

AI for SQL programming: queries and EXPLAIN checks · Zukko