SQL Formatting Best Practices for Readable Queries
SQL is read far more often than it is written — in code review, in incident debugging, in the "what does this report actually count?" conversation. Formatting conventions exist to make that reading fast. These are the practices most professional teams converge on, and the reasoning behind each.
1. Uppercase keywords, lowercase identifiers
SELECT u.id, u.name
FROM users u
WHERE u.active = 1;
Uppercase SELECT/FROM/WHERE makes the query's skeleton visible before you read a single identifier. Lowercase (snake_case) table and column names then read as content against that structure. The alternative convention — everything lowercase — is also defensible; what is not defensible is mixing styles in one codebase.
2. One clause per line
Each major clause starts a new line: SELECT, FROM, each JOIN, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT. This gives every query the same vertical shape, so a reviewer's eye goes straight to the clause that matters.
3. Explicit JOIN with ON, never comma joins
-- yes
FROM orders o
JOIN users u ON u.id = o.user_id
-- no
FROM orders o, users u
WHERE u.id = o.user_id
Comma joins bury the join condition in the WHERE clause, where forgetting it produces a silent cartesian product — one of the most expensive classes of SQL bug. Explicit JOIN ... ON makes a missing condition visually obvious.
4. Indent conditions under their clause
WHERE u.active = 1
AND u.created_at >= '2026-01-01'
AND (u.plan = 'pro' OR u.trial = 1)
Leading AND/OR at the start of each line (rather than trailing at the end of the previous one) means commenting out one condition never breaks the query, and diffs touch exactly one line per changed condition. Parenthesise mixed AND/OR explicitly — precedence bugs here are subtle and common.
5. Columns on separate lines when the list is long
Past three or four columns, one per line pays off in version control: adding a column is a one-line diff, and review comments can target an exact line. Same rule for GROUP BY lists.
6. CTEs over nested subqueries
WITH active_users AS (
SELECT id FROM users WHERE active = 1
),
recent_orders AS (
SELECT user_id, COUNT(*) AS n
FROM orders
WHERE created_at >= '2026-06-01'
GROUP BY user_id
)
SELECT a.id, COALESCE(r.n, 0) AS order_count
FROM active_users a
LEFT JOIN recent_orders r ON r.user_id = a.id;
Named CTEs turn a query into a readable pipeline of steps, each independently testable by selecting from it. Deeply nested subqueries convey the same logic inside-out, which is exactly how humans do not think.
7. Meaningful aliases
users u and orders o are fine at small scale; in a five-table join, usr, ord, pay beat a through e every time. Always alias aggregate columns (COUNT(*) AS order_count) — unnamed columns become someone else's guessing game downstream.
Make it automatic
Conventions only work when they are effortless. Paste any query into our free SQL formatter and it applies keyword casing, clause breaks and condition indentation in one click — string literals untouched, nothing uploaded.