How to format SQL queries for readability
A SQL query expresses intent, and the database does not care how it is laid out. People do. A query crammed onto one line runs exactly the same as one spread across twenty, but only one of them is readable in a pull request at midnight. Formatting is the cheapest way to make queries reviewable, debuggable, and safe to change.
Why formatting matters
The value shows up in three places. In review, a reader scans structure before logic: which tables, which joins, which filters. Consistent layout lets the eye jump to the part that matters. In debugging, a misplaced AND or a join condition hiding inside a long line is the kind of bug that consistent indentation surfaces immediately. And in diffs, a query with one column per line produces a clean change of a single line when you add a column, instead of a confusing rewrite of one giant line.
Conventions worth adopting
A house style does not need to be elaborate. A few consistent choices carry most of the benefit:
- Keyword case. Pick uppercase keywords (
SELECT,FROM,WHERE,JOIN) and lowercase identifiers, then never mix. Uppercase keywords let readers separate the language from your column and table names at a glance. - Major clauses on their own lines. Put
SELECT,FROM,WHERE,GROUP BY,HAVING, andORDER BYeach at the start of a line so the shape of the query is visible without reading it. - One column per line. In the
SELECTlist, give each column its own line. Adding or removing one becomes a one-line diff, and trailing-comma mistakes are easy to spot. - Indent and align JOINs. Keep each
JOINon its own line and put theONcondition just beneath it, indented. When conditions stack, align them so the comparison columns line up. - Indent nested logic. Subqueries and bracketed conditions get an extra level of indentation so their boundaries are obvious.
Before and after
The query below is correct but hostile to read:
select u.id, u.email, o.total from users u join orders o on o.user_id = u.id where o.status = 'paid' and o.created_at >= '2026-01-01' order by o.total desc;
The same query, formatted:
SELECT
u.id,
u.email,
o.total
FROM users AS u
JOIN orders AS o
ON o.user_id = u.id
WHERE o.status = 'paid'
AND o.created_at >= '2026-01-01'
ORDER BY o.total DESC;
Both return identical rows. The second version makes the join condition, the two filters, and the selected columns each findable in under a second.
Format before you commit
The cleanest workflow is to format right before committing, the same way many teams auto-format application code. That keeps the committed query in your house style and keeps diffs focused on real changes rather than whitespace churn. Some teams add a formatting step to their tooling so nobody has to remember.
Formatting changes whitespace, not meaning
This is the reassuring part. A formatter only rearranges whitespace, line breaks, and optionally keyword case. It does not reorder clauses, rename anything, or alter logic, so the query plan and the results are unchanged. Reformatting an existing query is safe to do at any time, including on queries you did not write, because you cannot change behavior by changing layout.
Doing this by hand on a long query is tedious, so let a tool do it. The SQL Formatter reformats a query in the browser with dialect and keyword-case options, and nothing is transmitted. When you are comparing two versions of a query, the Text Diff Checker shows exactly what changed, and the JSON Formatter & Validator does the same job for the JSON those queries often return.