Wrong Model, Bad SQL: Mistakes to Avoid in the Terminal
Sending vague prompts and getting fantasy columns
Most bad SQL does not come from a weak model, it comes from a weak prompt. When you ask for a query without handing over the schema, the model fills the gaps with plausible names, and plausible names do not exist in your database. The result looks right in the diff and fails on the first run with a missing column error, and you spend twenty minutes debugging a query that never had a chance of working.
Fix the input before you blame the model. Paste the table definitions that matter, the primary and foreign keys, the unique constraints, and a couple of representative rows when the data shape is unusual. If the schema is large, narrow it to the tables the query actually touches and describe the goal in plain language, including how fresh the data needs to be and which edge cases you care about.
- Include the exact table and column definitions for every table the query will touch.
- State the database engine and version so the model targets the right dialect.
- Describe the expected output shape, including column order and aggregation granularity.
- Add sample rows for ambiguous values like status codes, enums, and null semantics.
That extra context costs tokens, but it costs far less than a wrong query, and repeated context is exactly where caching helps. Fluxo bills per real token including cache reads and writes, with no monthly fee, so a schema you send again and again is written once and then read cheaply while you iterate.
Routing every SELECT through the most expensive model
Treating every task as a hard task is an expensive habit. A three-line select with one filter does not need a heavyweight reasoning model, and using one anyway adds latency to a loop you run fifty times a day while improving nothing about the SQL.
Keep the heavyweights for migrations, tricky joins across slowly changing dimensions, window functions with subtle frame clauses, and anything where a bug reaches production data. For routine work a fast and cheap tier is the right tool. DeepSeek V4 Flash handles ordinary selects, simple inserts, and formatting cleanups in seconds, while I keep Claude Sonnet 5.5 in the loop for the queries where the join logic is genuinely hard to reason about.
Switching tiers should not mean switching accounts, keys, or clients. Fluxo gives you one key and one base URL at https://api.fluxorelay.com for Claude, GPT, Gemini, DeepSeek, Kimi, GLM, Qwen, Grok and more, so the model name in your config is the only thing that changes when you step up or down. It works with Claude Code, Codex, Gemini CLI and any OpenAI SDK, which means the routing decision lives where you already edit and run commands.
The billing model rewards the same habit. You pay for real usage, including cache reads and writes, with no monthly fee, so an idle week costs nothing and a heavy migration week costs what it actually consumed. People you invite earn you a referral share when they pay, which quietly offsets the tokens your review loop burns.
Ignoring the SQL dialect, types, and time zones
Output that is correct in the abstract is not correct in your engine. Postgres, MySQL, Snowflake, BigQuery, DuckDB and SQL Server disagree about string functions, date arithmetic, boolean handling, and how nulls sort. Asking for SQL without naming the engine is how you end up with limit syntax where the engine wants top, or string concatenation that silently coerces a number.
Types matter just as much. Integer division truncates, decimals lose precision when pushed through floats, and timestamps without time zones drift by an hour the moment daylight saving changes. Tell the model which columns are numeric with scale, which are timestamps with offsets, and how you want nulls handled in aggregations, because coalesce decisions change results.
- Name the exact engine and version, including the SQL mode when behavior is configurable.
- Spell out how you want null values sorted, grouped, and aggregated in the result.
- Ask for numeric handling that preserves decimal precision for money and measurements.
- Specify the time zone and timestamp semantics for every date comparison in the query.
Ask for a query that is valid in that dialect and for a short note listing any assumption the model made about types or semantics. Assumptions are where silent wrong answers live: a filter that compares a timestamp to a string can run and still return the wrong rows, which is far worse than an error.
Reviewing a query without its execution plan
Asking a model to review a query in a vacuum produces generic advice you could have written yourself. It will mention indexes and suggest selecting only what you need and then move on, because it has no idea whether your tables hold a million rows or a billion, or whether the filter is selective at all.
A useful review starts with evidence. Paste the query, the schema, the estimated row counts, the indexes on the touched columns, and the output of an explain analyze run in a staging environment. With that, the review can point at the specific join order, the nested loop that should be a hash, or the cast that disables an index.
- Include the real execution plan with timing and row estimates for every relevant query.
- Share index definitions and approximate table sizes so the estimates can be trusted.
- Ask for findings ranked by impact rather than an undifferentiated list of opinions.
- Request one concrete rewrite along with a note on how to measure the improvement.
Treat the rewrite as a hypothesis, not a patch. Run it against a fixture with realistic volume, compare timing and row counts with the original, and keep the old query around until the numbers agree. A reviewer that cannot measure its own suggestion is only guessing politely.
Stopping at one answer instead of looping the review
The most expensive mistake is accepting the first response. One pass gives you a plausible query, and plausible is exactly the standard that lets a rare edge case through. Ask the model to critique its own output before you run it, specifically looking for null handling, duplicate rows from one-to-many joins, and filters that drop rows you meant to keep.
Then run the query against a small fixture with known results and compare the output row by row. If the counts disagree, send the expected and actual results back with the schema and ask for a diagnosis rather than another guess. Two focused corrections usually beat ten blind rewrites.
- Ask for a self-critique that targets null handling, duplicate rows, and filter boundaries.
- Compare query output against fixtures with known expected rows before touching production.
- Send mismatches back with schema context so the next answer addresses the real defect.
- Keep generated SQL in version control and treat every accepted query as reviewable code.
Keep your secrets out of that loop. Desensitize connection strings and credentials before they leave your machine, prefer schema and sample data over production dumps, and remember that generated SQL is code that deserves the same scrutiny as anything else you merge. If you hit a workflow question specific to your setup, you can leave a note on the contact page after signing in.
If you want to try the multi-model loop from where you already work, create a Fluxo account, point your OpenAI SDK at the base URL, and start with the smallest model that can answer the question.