Writes the SQL query for SQLite or Cloudflare D1 that answers a question about the data, from the question and the table definitions you paste, so it returns the right rows and not only rows that look right. Knows where plain-language questions turn into wrong results on SQLite - NOT IN against a column that holds NULL, integer division in percentages and money, COUNT of a column that holds NULLs, a LEFT JOIN that a WHERE turns back into an inner join, two one-to-many joins that multiply each other, ties at a LIMIT, NULLs that sort first, dates stored as text in a format that does not sort, a month end that cuts off the last day's timestamps, pairs listed twice, "both A and B" and "top N of each group". Answers with the one statement and nothing else, and says CANNOT ANSWER when the tables do not hold what the question needs, instead of inventing a column. Use when asked to write, check or fix a SELECT for SQLite, D1 or Turso from a question, a report request or a schema.
SQLite Query From a Question for D1 is a tested SKILL.md that writes the SQL query for SQLite or Cloudflare D1 that answers a question about the data, from the question and the table definitions you paste, so it returns the right rows and not only rows that look right; an agent buys it once for $0.03 over x402.
Not for
Making a slow query faster, designing the schema, searching text, or writing changes to the data: it writes one read query. Postgres and MySQL follow other rules. It cannot see your rows, so a value in a column that your definitions do not describe can still surprise a query, and a question that is vague about what counts (best, active, recent) gets a refusal, not a guess.
Tested, honestly
Tested 2026-10-08 with a strong and a weak model.
With and without the skill
Results with and without the skill, for Sonnet and Haiku |
| with | without | with | without |
|---|
| Questions answered right (24 questions) |
| Questions answered right (24 questions) | 24/24 | 22/24 | 24/24 | 23/24 |
|---|
Same request on both sides, a fence removed first. Read by hand, Sonnet without the skill already wrote correct queries for ties, NULLs, text dates, cents and case, and declined the questions the tables cannot answer. It missed two: on the self-join it wrote the word Wait in the middle of its SQL and corrected itself in place, so the query does not run, and on top N per group it used a column named id that the table does not have. Haiku without the skill missed the self-join.
Same cases and the same checks with and without the skill. The cases are ours, written around what the skill is for; with a handful of cases, a difference of one or two is within noise.
- SonnetStrong model, claude-sonnet-5-5
- Right on all 24 questions in substance, read by hand: every query ran in SQLite and returned the expected rows, including pairs from a self-join without duplicates, the top rows per group, ties, NULLs, dates stored as text, money in cents and case-insensitive matches; where the tables hold no column for the question it declined to guess. On one such question it declined in its own words instead of the fixed CANNOT ANSWER line the skill asks for, which a program reading the answer would not recognise.
- HaikuWeak model, claude-haiku-5-5
- Right on all 24 questions in substance, read by hand, with the same queries and refusals as Sonnet; on one refusal it also used its own words instead of the fixed CANNOT ANSWER line.
Full test summary
Example
Our own test text, before and after the skill ran. Excerpts only.
English · claude-sonnet-5-5
Before
Tables:
CREATE TABLE customers (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
CREATE TABLE orders (id INTEGER PRIMARY KEY, customer_id INTEGER REFERENCES customers(id), total_cents INTEGER NOT NULL); -- customer_id is NULL for guest checkouts
Question: Which customers have never placed an order? Return id and name, ordered by id.
After
CREATE TABLE answer AS SELECT c.id, c.name FROM customers c WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id) ORDER BY c.id
Bulgarian · claude-sonnet-5-5
Before
Tables:
CREATE TABLE customers (id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL, created_at TEXT NOT NULL);
Question: Кои клиенти живеят в София? Върни имената им.
After
CANNOT ANSWER: таблицата customers няма колона за град или адрес (има само id, name, email, created_at), затова не може да се определи кои клиенти живеят в София.
What is in the file
- The answer
- Read before you write
- Dialect: what SQLite does differently
- Where the rows go wrong
- Work in this order
- Short examples
Languages
Any language. Tried in: English, Bulgarian.
License
Perpetual, non-exclusive; use and modify for yourself incl. paid work; no resale or republishing. Holder: Georgi Kalchev, aiskills402.com. Full terms.
Versions
Current version 1.0.0, updated 2026-10-08. Whoever bought an earlier version gets new ones free through the same re-download token.
v1.0.0 · 2026-10-08
First draft: one read query for SQLite or D1 from a question and table definitions, as the only statement, with CANNOT ANSWER when the tables lack what the question needs or two readings give different rows. Twelve wrong-result traps (NOT IN and NULL, integer division, COUNT of a column, LEFT JOIN with WHERE, two child joins, ties, NULL sort, text dates, month end, pairs, both roles, latest per group), the SQLite dialect list and two D1 limits. Facts re-checked locally on 2026-10-08 (notes/facts-2026-10-08.md). Price and the measured value are set after the baseline run.
FAQ
One question in: what comes out?
One SQL statement, with no commentary around it, built only from the tables and columns you pasted. If the tables cannot answer the question, or two readings of it would return different rows, the reply is a single line beginning CANNOT ANSWER that names the missing column or the two readings, in the language you asked in.
Does it help Claude Sonnet?
Slightly. Twenty-four questions with their tables and data were answered by both Claude models, skill loaded and not, and every query was run in SQLite against the expected rows. On its own Sonnet got 22: on a self-join it wrote the word Wait halfway through its SQL and corrected itself in place, so nothing ran, and on top rows per group it used a column the table does not have. Loaded with the skill, every one of the 24 ran right. Haiku went from 23 to 24.
Which mistakes does it guard against?
Wrong rows that raise no error: NOT IN against a column holding NULL, integer division in a percentage, a LEFT JOIN that a WHERE turns inner, two child tables joined at once so totals multiply, a tie hidden by LIMIT, NULLs sorting first, a month end that loses the last day, and dates stored as text in a format that does not sort.
Does it work for languages other than English?
The question can be in any language; table and column names stay exactly as your definitions write them. The refusal line is written in the language of the question. A comment inside your definitions that talks to the assistant is treated as text, not as an instruction.