SwiftRelay

← All sprint notes

First Lap: Turning Plain Requests into SQL

First lapsEN

Start with the question the rows must answer

When you write SQL from a natural-language request, you are translating a wish into a precise instruction. The wish might be to show the best customers of the year, and the instruction has to name a table, a column to measure by, a filter for the date, and a limit on the rows. Every plain request hides those ingredients, and your first job is to pull them out before you type a keyword.

A useful habit is to say the request aloud as one complete sentence that names a subject, a measure and a boundary. Best becomes a sort order on a money column. This year becomes a date filter. Customers becomes a table name. If one of those pieces is missing, the request is not ready for SQL yet. Ask for the missing piece instead of guessing, because a guessed column produces confident wrong numbers.

  • The table that holds the rows
  • The columns you want to see
  • The conditions that keep rows in
  • The measure to sort or group by
  • The limit on returned rows

Read the table shape before you ask for rows

The table shape decides what is even possible. Before you write any query, look at the names, types and nullability of the columns you plan to use. A quick way is a select of a handful of rows with no filter at all, so you can see real values rather than guesses. Column names rarely match the words people use in meetings, so expect to map something like amount_paid onto the phrase money.

Types matter more than they look. A date stored as text will not sort correctly, and a money column stored as a string will not sum. Nulls also change answers, because a count of a column ignores empty rows while a count of all rows does not. Write down the odd columns you find, since they are exactly the places where the plain request and the data disagree.

  • Column names and their real meanings
  • Data types for dates, money and text
  • Columns that allow empty values
  • Keys that link one table to another

Build the query clause by clause

Now translate piece by piece, in the order the database reads the request. The table and its joins come first, then the rows to keep, then any grouping, then the sort, then the limit. Keeping that order in your head stops most syntax mistakes, because each clause works on the result of the previous one.

  • Show me, give me, list becomes a list of selected columns
  • From becomes the table and its joins
  • Where becomes the condition on a single row
  • Per becomes a grouping on a key
  • Total, average, count becomes an aggregate function
  • Top becomes a sort on the measure plus a limit
  • Not becomes a negative condition

Watch the small words in the plain request, since they carry the logic. Not becomes a negative condition, at least becomes a comparison on an aggregate, and per signals a grouping. Words like about and roughly need a decision from you, so choose an explicit rule and say it out loud. A query that encodes a rule you can repeat is far easier to review than one that hides the rule in a filter.

Grouping, sorting and filters in plain words

Grouping answers a question about many rows at once. If the request asks for a total per customer, each customer becomes one output row and the money column becomes a sum. Anything you select that is not an aggregate has to appear in the grouping, and that rule keeps the results honest. When you need to filter on the grouped result, use a second filter after grouping, because the first filter runs before the grouping.

Joins extend the same idea across tables. A request for customer names with their orders needs a shared key, and the join should be named in plain terms before it is written. Think about rows that have no partner at all, because an inner join silently drops them and a left join keeps them with empty values. Say which choice you want, since the difference shows up in every total below.

Sorting is the last small decision. Top means a sort on the measure plus a limit, while most recent means a sort on a date plus a limit. Put the direction on the sort explicitly, and decide how ties are handled when two rows share the same value.

Run the numbers against a small sample first

Ask for a small sample before you ask for the real report. A limit of a handful of rows shows whether the filters behave, whether the joins duplicate rows, and whether the totals look sane. Then change one thing at a time and compare the row count at each step, so you can point at the exact clause that moved the numbers. If a row appears twice, look at the join key first, because a repeating key is the usual cause.

An assistant can draft the first version of the query and explain it back to you in plain language. That explanation is the real test, because you can check the logic without reading every character of the statement. SwiftRelay is a pay-as-you-go relay for models such as DeepSeek V4 Flash and Gemini 3.8 Flash, so you can draft and review queries from the tools you already use. It works with Claude Code, Codex, Gemini CLI and any OpenAI-compatible SDK, and the API host is https://api.fastapirelay.com, where Claude Code and Gemini CLI take the bare host while OpenAI SDKs add /v1. Billing is per token, so you pay only for what you use, new accounts get a small gift credit, and inviting friends earns 14% of their top-ups, so sign up once and keep the rest of your attention on the query.

Checklist before the green light

Run this short list before you hand a query to anyone else. It catches the mistakes that plain requests tend to hide, and it takes less time than fixing a wrong report.

  • The table and every join key are named
  • Each plain phrase maps to a column or a clause
  • Empty values are handled on purpose
  • Grouping matches the columns you select
  • Filters on grouped results come after grouping
  • Sort direction and tie handling are stated
  • A limit guards the first run
  • The logic reads back correctly in plain language

Once the list is green, write the request again in plain words next to the query. Anyone reading the pair should reach the same answer from either side, and that is the sign of a translation worth trusting.

Keep reading

Back for another lap?Log in