SQL Query Generator
Describe the rows you want and get the SQL, with an explanation of how it was assembled. The built-in engine handles the common shapes — aggregates, per-group breakdowns, top-N rankings, trends over time, and negatives such as customers with no orders — and tells you plainly when your description has gone past what it can safely infer.
Refusing to Guess Is the Feature
A query generator that invents a table name produces SQL that fails on execution, which is annoying but survivable. One that invents a join condition produces SQL that runs and returns a plausible wrong number, which is not. The design decision at the centre of this tool is that the built-in engine only writes queries it can justify, and returns an explicit message asking for your schema when it cannot.
- Five query shapes are recognised from the wording: an aggregate with a group-by dimension, a count, a trend over time, a top-N ranking, and a negation such as "customers with no orders". Each is classified before any SQL is written, and the result card names the pattern it chose.
- Negation is handled as a correlated NOT EXISTS subquery rather than a LEFT JOIN with a null check, because the two are equivalent in result but the subquery states the intent in the query itself, which matters when someone else reads it later.
- An optional schema box takes tables one per line in the form orders(order_id, customer_id, amount, date). With it filled in, column choice stops being a guess: the value column is picked by looking for amount, revenue, sales, price, value, total, quantity or cost in your actual column list; the date column by looking for anything matching date, time, _at, _on, month, created or timestamp; and the grouping column by looking for a name-like column such as name, label, title, city or region.
- Five dialects are available. The difference is real rather than cosmetic. BigQuery takes DATE_TRUNC with the column first and the unit unquoted; MySQL has no equivalent that works across versions, so the engine emits DATE_FORMAT with a year-month pattern and leaves the intended granularity as a comment; Postgres and the standard option use the familiar quoted-unit form.
- The result card reports the complexity of the query it produced — basic SELECT, GROUP BY aggregate, time-bucketed aggregate, GROUP BY ranking with a limit, or a correlated NOT EXISTS subquery — so you can see the shape before you run it.
- Every query comes with an explanation and the ordered steps that were followed, which is what lets you check the join condition rather than trusting it.
Writing a Request the Engine Can Act On
- State the measure, the grouping and any filter in one sentence — "show total order amount per customer for last month, only customers with more than three orders" contains all three, and the engine reads them as an aggregate with a having-clause and a date filter.
- Turn on the schema box if you have the column names to hand. Paste one table per line as table(column, column, column). This is the single change that most improves the output, because it converts every column reference from a heuristic to a lookup.
- Leave Query kind on Auto-detect unless the engine has classified your request in a way you disagree with. Overriding it to Trend over time or Top N ranking is a faster correction than rephrasing the sentence.
- Pick your dialect before generating, not after. The date truncation differs, and swapping the chip afterwards regenerates rather than translating — which is the correct behaviour, since a patch to a finished query is how dialect bugs get in.
- Read the steps list before copying. If a step says a column was chosen by name pattern, that is the line to verify against your real table.
- If the card comes back asking for schema, that is not a failure. Add the tables, or connect a provider, and generate again.
The Three Ways This Tool Answers
| Alternative | Notes |
| Built-in engine, schema supplied | The strongest case. The request is classified, your actual column names are used, and the query is assembled from the same patterns every time. Results are reproducible, which means a query generated in March and one generated in June for the same request will match. |
| Built-in engine, no schema | Still produces correct SQL for the recognised shapes, but column names come from a keyword preference list and generic entity words like orders, products and customers. The query will run against a schema with conventional naming and needs renaming against one with house style. The engine's own message tells you when it has reached this point. |
| Explicit refusal | When the request needs an aggregate table the engine cannot name, it stops and asks for schema context or a provider key instead of emitting something that looks finished. This is the outcome that saves the most time in practice, because the alternative is debugging a plausible query that silently dropped a join. |
| AI provider, connected by you | Handles requests outside the five recognised shapes and produces tailored column choices. When no schema is given it is instructed to use clearly generic table names and to say so, rather than inventing a specific-sounding schema that does not exist. |
One Request Across Two Dialects
Take "monthly revenue trend for the last year" with the schema orders(order_id, customer_id, amount, date) supplied, and follow what changes when the dialect changes.
- The engine classifies this as a trend, which it reports as a time-bucketed aggregate. It looks for a date column matching its pattern list and finds date; for the measure it looks for amount, revenue or sales and finds amount.
- On Postgres or the standard option the bucket becomes DATE_TRUNC('month', date). This is the form most people expect and the one every tutorial shows.
- On BigQuery the arguments are reversed and the unit is not quoted: DATE_TRUNC(date, MONTH). Copying the Postgres form across is a mistake that produces an error rather than a wrong answer, which is the forgiving kind.
- On MySQL the emitted form is DATE_FORMAT(date, '%Y-%m-01') with the intended granularity carried in a trailing comment. This is worth understanding rather than skimming, because the expression produces a first-of-month date used as a grouping key, not a truncated timestamp, and grouping by it and by the original column give different results.
- The dimension and value column choices are identical across all three dialects, so only the bucket expression changes. That separation is what makes switching dialects safe: it is one line, not a rewrite.
When to Use This and When to Write It Yourself
- Fast first drafts of reporting queries where the shape is conventional and the value is in getting the join and the grouping right quickly.
- Checking your own instinct on a negation. If you were about to write a LEFT JOIN with a null check, comparing it against the NOT EXISTS form is a useful two-minute exercise.
- Translating a query between dialects, using the generated output as a reference for the date function rather than as a finished artefact.
- Not a tool for window functions, recursive queries, or anything involving a specific database's extensions. The recognised shapes are conventional aggregates, and a request outside them returns a refusal rather than a creative attempt.
- Not a tool for query optimisation. It writes a correct query for the shape you described; it does not know your indexes.
- Not a place to paste a production schema you are not able to share. The built-in path keeps everything in the page, so the schema stays with you; connecting a provider sends it.
Questions About the Generated SQL
Why did it refuse instead of writing the query?
Because the request needed to aggregate against a table the engine could not identify, and it will not invent a table name for an aggregate. The message asks for two possible routes: paste your tables and columns into the schema box, or connect an AI provider for a tailored query. Both are real answers; the refusal is not a dead end, it is the tool declining to produce something that runs and returns a wrong number.
How are column names decided when I do not supply a schema?
By pattern. The engine prefers a value column named amount, revenue, sales, price, value, total, quantity or cost; a date column containing date, time, _at, _on, month, created or timestamp; and a grouping column that looks like a label, such as name, title, city or region. That covers conventionally named schemas well and house-style naming badly, which is exactly why the schema box exists.
Is the MySQL date expression really equivalent to the others?
No, and it is worth being explicit about that. The Postgres and BigQuery forms return a truncated timestamp; the MySQL form returns the first day of the month as a date. Grouping by either gives monthly buckets, but the value carried into the select list differs, so a report that displays the bucket will show a different string depending on the dialect. Adjust the select list if the bucket is visible in your output.
Can I paste my real schema into this?
In the built-in mode the schema text is parsed inside the page and never transmitted, so it stays on your machine and you can verify that in the network panel. If you connect an AI provider and generate, the schema context is part of what gets sent to that provider. For a schema under a confidentiality obligation, stay on the built-in path and accept the narrower range of shapes it handles.
Last verified: October 3, 2026
Launch SQL Query Generator
All Tools · AI Tools · About · How We Test · Contact · Privacy
Maintained by Huang Bin · Last updated October 3, 2026