Skip to main content
{/}codefunc
GuidesJuly 26, 2026 · 5 min read

SQL Formatting for Readable Queries: Reviews, Dialects, and Migration Safety

How to format SQL for code review clarity, where dialects disagree, and when you should not reformat production migrations or generated queries.

By codefunc

sqlformattingcode-reviewmigrationspostgres

Unreadable SQL slows reviews more than clever joins do. A 80-line query in one horizontal blob hides the WHERE that deletes too much; a consistently formatted query makes intent obvious. Formatting is not the same as rewriting — and reformatting the wrong files can create noisy diffs or, worse, change meaning when a dialect-sensitive tool "fixes" syntax.

Bottom line: Format SQL you own for human review — keywords, indentation, and clause order. Know your dialect (PostgreSQL, MySQL, SQLite, BigQuery). Do not bulk-reformat committed migrations, vendor dumps, or generated ORM SQL unless the team explicitly agrees. Use a formatter for clarity; use tests and EXPLAIN for correctness.

Who this guide is for

Backend and full-stack developers writing hand-crafted SQL, reviewing PRs with queries in strings, or cleaning analytical SQL before sharing. Frontend engineers who touch reporting queries or Supabase/RPC SQL will get the same wins.

Why readability beats golf

SQL is declarative, but humans still read top-to-bottom:

-- Hard to review
SELECT u.id,u.email,count(o.id) FROM users u LEFT JOIN orders o ON o.user_id=u.id WHERE u.created_at>NOW()-INTERVAL '30 days' AND u.status='active' GROUP BY u.id,u.email HAVING count(o.id)>0 ORDER BY count(o.id) DESC LIMIT 50;

-- Reviewable
SELECT
  u.id,
  u.email,
  COUNT(o.id) AS order_count
FROM users AS u
LEFT JOIN orders AS o
  ON o.user_id = u.id
WHERE u.created_at > NOW() - INTERVAL '30 days'
  AND u.status = 'active'
GROUP BY
  u.id,
  u.email
HAVING COUNT(o.id) > 0
ORDER BY order_count DESC
LIMIT 50;

What reviewers scan for:

Clause Questions
FROM / JOIN Are join keys correct? Inner vs left?
WHERE Is this accidentally too broad?
GROUP BY Do selected columns match aggregates?
ORDER BY / LIMIT Stable pagination? Deterministic order?

Paste messy SQL into the SQL Formatter before review when the author did not format it — client-side formatting keeps proprietary queries on your machine.

A practical formatting style

Teams vary; pick conventions and stick to them.

Rule Recommendation
Keywords Uppercase SELECT, FROM, WHERE (common) or all lowercase — not mixed
Indentation One level per clause / subquery
Columns One per line when there are more than three
Joins JOIN + ON on following indented lines
Boolean logic Align AND / OR under WHERE
Subqueries Parentheses on own lines for large subselects
Trailing commas Allowed in some warehouses (BigQuery); invalid in others — know your engine

Consistency matters more than winning a style argument. Match the dominant style in the repo.

Dialect differences that break formatters

SQL is not one language. Formatters and linters need a dialect hint.

Dialect Gotchas
PostgreSQL :: casts, ILIKE, RETURNING, JSON operators ->, ->>
MySQL Backticks, LIMIT offset, count, different UPSERT story
SQLite Dynamic types; fewer window/function conveniences in older versions
SQL Server TOP, bracket quoting [Table], OUTER APPLY
BigQuery Trailing commas, backticks, scripting features

A PostgreSQL-specific cast:

SELECT payload->>'email' AS email
FROM events
WHERE created_at >= '2026-01-01'::timestamptz;

Running that through a MySQL-oriented formatter or executor will fail or mangle tokens. Set the dialect explicitly in your tool when options exist; the SQL Formatter is for readability passes — still validate in the target engine.

JSON in SQL workflows

APIs and warehouses increasingly mix SQL with JSON payloads. When debugging:

  1. Format the SQL with SQL Formatter.
  2. Extract JSON literals or sample documents and format with JSON Formatter.
  3. Compare keys carefully — SQL JSON path syntax differs by vendor.
-- Postgres: filter on JSON field
SELECT id
FROM invoices
WHERE (data->>'status') = 'paid';

Readable SQL plus pretty JSON samples in the PR description cuts "what does this payload look like?" back-and-forth.

When not to reformat

Formatting changes are still diffs. Avoid drive-by reformat in these cases:

Artifact Why leave it alone
Versioned migrations already applied Reordering/rewrapping creates huge diffs; risk of accidental edits
Expand/contract migration pairs Noise obscures the real schema change
ORM-generated SQL in snapshots Regenerate from the ORM; do not hand-pretty if tests compare strings
Vendor schema dumps Treat as upstream; wrap or view separately
Query strings matched in brittle tests Update tests deliberately or assert on parsed AST / behavior

Safer policy:

  • Format new SQL in application code and views.
  • For old migrations, leave history untouched; put improved versions in new migrations if logic changes.
  • In PRs, separate "format only" commits from behavioral changes when a large cleanup is unavoidable.

Never "just format" a migration file in the same commit as a logic change. Reviewers will miss the WHERE that moved.

Formatting vs correctness

A pretty query can still be wrong. Formatting does not fix:

  • Missing join predicates (accidental Cartesian products)
  • NOT IN with NULLs
  • Timezone-skewed timestamp filters
  • Index-unfriendly wrappers on columns (WHERE LOWER(email) = … without a matching index)

Use formatting for review velocity; use explain plans, tests, and staging data for correctness.

Workflow that scales

  1. Write or paste the query.
  2. Run SQL Formatter (correct dialect mindset).
  3. Self-review clauses top to bottom.
  4. For JSON-heavy queries, pretty-print samples with JSON Formatter.
  5. Commit with a message that says whether the change is format-only or behavioral.
  6. Leave applied migrations alone unless you are fixing a bug in a new migration.

Practical takeaway

Format SQL so reviewers can see joins and filters in seconds. Respect dialect differences, and keep formatters away from frozen migrations and brittle string snapshots unless the team plans a cleanup. codefunc's SQL Formatter and JSON Formatter run in the browser for quick, private cleanup before you paste into a PR or ticket.

Sources

Try it on codefunc

Related articles

3

Search tools

Find a developer tool