SQLFormat SQL Formatter

SQL Formatting Best Practices: Queries Your Team Can Still Read in Six Months

SQLFormat — Free Online SQL Formatter, Beautifier & Minifier Guides · Updated 2026-10-01 · All guides

A formatted query is not a pretty query; it's a query whose structure you can verify at a glance. Formatting is how reviewers check join order and how you spot the missing AND before production does. These rules are opinionated on purpose — consistency beats personal taste in a shared codebase.

The cadence rules

One clause per line, one item per line when the list is load-bearing:

WITH active_users AS (
  SELECT id, email, created_at
  FROM users
  WHERE status = 'active'
    AND created_at >= NOW() - INTERVAL '90 days'
)
SELECT
  u.email,
  COUNT(o.id) AS total_orders
FROM active_users AS u
LEFT JOIN orders AS o ON o.user_id = u.id
GROUP BY u.email
HAVING COUNT(o.id) > 5
ORDER BY total_orders DESC
LIMIT 50;
  • Major clauses (WITH/SELECT/FROM/JOIN/WHERE/GROUP/HAVING/ORDER) start a new line, uppercase. Eyes scan for structure, not words.
  • AND/OR indented under WHERE so a missing boolean operator between filters is visually loud.
  • Select-list items one per line with trailing commas — diffs touch one line, not the whole list.
  • Every table gets a short explicit alias (orders AS o), and every column reference is qualified. status means nothing in a five-table join; u.status means one thing forever, even after someone adds a second status column.

Format at paste time, not review time

The moment a query arrives as a single line from a log, ORM debug output, or a colleague's chat, run it through the formatter first. Reviewing a one-liner means mentally parsing before you can start judging — you'll approve the structure you failed to read. The Postgres and MySQL routes preset dialect-appropriate samples, and the formatter runs entirely client-side, so pasting production query text doesn't ship it anywhere.

Indent joins, don't flatten them

FROM orders AS o
LEFT JOIN users AS u ON u.id = o.user_id
LEFT JOIN regions AS r ON r.id = u.region_id

Flat joins read as one long sentence and hide outer-join asymmetry — the class of bug where a LEFT JOIN silently becomes an inner join via a WHERE filter on the null side (WHERE r.country = 'US' belongs in the ON clause or as (r.country = 'US' OR r.country IS NULL)). Structure that shows the ON clauses makes that mistake harder to commit. See patterns and gotchas for the null-side trap in full.

Keywords up, identifiers as-written

SELECT COUNT(*) FROM t — keywords uppercase signals "syntax here," identifiers lowercase signals "data there." It's the one style rule with near-universal buy-in because it doubles as a diff aid: uppercase tokens in a diff line mean logic moved.

Minify for embedding, format for humans

The same SQL has two audiences. Interactive reading: formatted, as above. Migrations, embedded queries in SQLite, code where the query is a string constant: minified to one line keeps code review diffable and the query greppable. Our SQLite minifier route produces the compact single-line form; keep the formatted canonical copy in a .sql file and generate the embedded string, rather than hand-collapsing.

Comments beat cleverness

A five-line CTE chain with no comment about why each step exists is a re-derivation waiting to happen. Name CTEs after their meaning (active_users, not t1), and when a query genuinely needs narration, put the note in the file next to it, uppercase-clause aligned.

FAQ

Comma-first or comma-last? Pick one for the repo. Comma-first makes "delete this column" edits safe (the last item's missing comma error lands on the removed line) and is traditional in DBA shops; comma-last matches every other language. The rule that matters is the repo-wide one.

Should formatters rewrite my aliases or casing? No. A formatter that renames things is a refactor wearing a costume — it destroys blame history and can break quoted-case identifiers ("MyTable" in Postgres is case-sensitive; folding it to lowercase is a behavior change, not a style change).

Why does the formatted query have different results? It can't. Formatting is whitespace-only and the tool is parse-verified — if results ever differ, the original contained a trailing semicolon-separated statement or the client was doing something to the string; check both.

Developer Sponsor / Partner
Copied to clipboard!