SQL Formatters: A Guide to Best Practices
Well-formatted SQL is easier to debug, easier to review, and less likely to hide bugs in plain sight. This guide covers what good SQL formatting looks like and how to maintain it consistently.
Why SQL formatting gets ignored
SQL often gets written in a hurry. Queries are typed into a console, copied from Stack Overflow, generated by an ORM, or built up iteratively with conditions added one at a time. By the time a query works, it often looks like one long line with inconsistent casing and no structure.
When the query is short and simple, this doesn't matter much. When it joins six tables, has a nested subquery, and includes a CASE statement, the formatting is the difference between being able to reason about it and having to run it to find out what it does. Most SQL bugs in production aren't logic errors in isolation — they're logic errors that were hidden by formatting that made the query hard to read.
Keyword casing: pick a convention and apply it consistently
The most visible formatting decision in SQL is whether to write keywords in uppercase or lowercase. SELECT vs select. WHERE vs where. Neither is technically required — SQL is case-insensitive for keywords — but uppercase keywords are the long-standing convention and the one most formatters default to.
The reason uppercase keywords persist is practical: they make the structure of a query scannable. When SELECT, FROM, JOIN, WHERE, and GROUP BY all stand out visually, you can locate the clause you're interested in without reading the whole query. Lowercase everything looks uniform in a way that makes structure harder to identify quickly.
Clause alignment and line breaks
Each major clause should start on its own line. A query that spans multiple lines with each clause on its own line is dramatically easier to read than the same query collapsed.
SELECT
u.id,
u.name,
o.amount
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.amount > 100
ORDER BY o.amount DESC;
This layout makes it obvious at a glance what's being selected, where it's coming from, and what conditions apply. The same information in one line requires the reader to parse character by character.
Indent the columns in a SELECT list. When you're selecting twenty columns and one of them is causing an issue, indentation makes it straightforward to find the relevant line.
JOIN conditions deserve their own line
Multi-table queries with several JOINs are where formatting matters most. Each JOIN and its ON condition should be clearly associated. A common pattern is to put the JOIN keyword at the start of a new line and the ON condition on the same line or indented below it:
SELECT u.name, p.title, c.name AS category
FROM users u
INNER JOIN posts p ON u.id = p.user_id
INNER JOIN categories c ON p.category_id = c.id
WHERE u.active = 1;
When the ON condition is long or involves multiple predicates, put each predicate on its own line and align them:
INNER JOIN order_items oi
ON o.id = oi.order_id
AND oi.deleted_at IS NULL
Using a formatter vs writing formatted SQL
The best workflow is to write SQL however it comes naturally and then run it through a formatter before committing or sharing it. Trying to format as you type is slow and inconsistent — formatters do it faster and more accurately than anyone does manually.
For queries you're building incrementally in a console or query tool, use a formatter when you're done. Paste the finished query into the SQL Formatter, clean it up, then paste it back. It takes thirty seconds and makes the query significantly easier to review.
For queries checked into a codebase, treat SQL like any other code: format it, review it, and keep it readable. A query that lives in a migration file or a repository will be read many times. The thirty seconds you spend formatting it upfront is worth it.
Aliases: use them, name them well
Table aliases are essential in multi-table queries — writing the full table name in every column reference is verbose and makes queries harder to scan. But aliases should be meaningful. u for users and o for orders is fine. Single-letter aliases for tables with non-obvious names (a, b, x) make queries harder to read, not easier.
Column aliases in SELECT lists should describe what the value represents, not just shorten it. COUNT(*) AS order_count is clear. COUNT(*) AS c forces the reader to figure out what c means in context.