Why SQL formatting matters
A SQL query that works and a SQL query that is easy to work with are not the same thing. Unformatted SQL is difficult to read, hard to debug, and error-prone to modify:
select u.id,u.name,o.total from users u join orders o on u.id=o.user_id where o.total>100 order by o.total desc
That query fits on one line, but finding the join condition or adding a WHERE clause requires careful reading. Formatted:
SELECT
u.id,
u.name,
o.total
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.total > 100
ORDER BY o.total DESC
The logic is immediately visible. Adding a new column or a second join condition takes seconds.
Formatting SQL does not execute the query or validate it against a database schema. A formatted query can still have logical errors or reference columns that do not exist.
SELECT
Put the SELECT keyword on its own line. List each column on a separate line, indented. This makes it easy to add, remove, or comment out individual columns without disrupting the rest.
SELECT
user_id,
first_name,
last_name,
created_at
FROM users
For a SELECT *, it is acceptable to keep it on one line — but in production code, prefer naming columns explicitly so your code is resilient to schema changes.
Aliases should follow the column immediately:
SELECT
u.id AS user_id,
u.created_at AS registered_on,
COUNT(o.id) AS order_count
JOIN
Place each JOIN on its own line at the same indentation level as FROM. The ON condition follows on the same line, or on the next line if the condition is long.
FROM orders o
JOIN users u ON o.user_id = u.id
JOIN products p ON o.product_id = p.id
LEFT JOIN discounts d ON o.discount_id = d.id
Use explicit JOIN types: INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN. Avoid bare JOIN with no modifier — while JOIN defaults to INNER JOIN in SQL, being explicit removes ambiguity.
WHERE
Start WHERE on a new line. Place each condition on its own line with the logical operator (AND / OR) at the start of the line:
WHERE
o.status = 'completed'
AND o.total > 100
AND u.country = 'GB'
Leading AND / OR makes it easy to comment out a condition during debugging without needing to worry about whether it was at the start or end of the clause.
Grouped conditions with parentheses:
WHERE
(u.role = 'admin' OR u.role = 'moderator')
AND u.active = true
GROUP BY and HAVING
SELECT
country,
COUNT(*) AS user_count
FROM users
WHERE active = true
GROUP BY country
HAVING COUNT(*) > 50
ORDER BY user_count DESC
List each group-by column on a separate line if there are multiple:
GROUP BY
country,
plan_type
ORDER BY
ORDER BY
last_name ASC,
first_name ASC,
created_at DESC
Always specify ASC or DESC explicitly — default ordering is ascending, but making it visible prevents confusion.
Nested queries and subqueries
Indent the subquery one level deeper than the surrounding query. Wrap it in parentheses and alias it:
SELECT
u.name,
recent.last_order_date
FROM users u
JOIN (
SELECT
user_id,
MAX(created_at) AS last_order_date
FROM orders
WHERE status = 'completed'
GROUP BY user_id
) AS recent ON u.id = recent.user_id
For complex subqueries, consider a Common Table Expression (CTE) with WITH, which moves the subquery to the top and gives it a meaningful name:
WITH completed_orders AS (
SELECT
user_id,
MAX(created_at) AS last_order_date
FROM orders
WHERE status = 'completed'
GROUP BY user_id
)
SELECT
u.name,
co.last_order_date
FROM users u
JOIN completed_orders co ON u.id = co.user_id
CTEs often make complex queries far more readable than deeply nested subqueries.
General readability tips
Capitalise keywords. SELECT, FROM, WHERE, JOIN, ON, AND, GROUP BY — all in uppercase. Column names and table names in the case used by your schema. This separation makes the SQL structure visible at a glance.
Align aliases. If your editor supports it, align the AS keyword in a column list for visual clarity (optional — some style guides prefer not to).
Use meaningful aliases. u for users and o for orders is fine. a for orders is confusing.
Consistent spacing around operators. Write o.total > 100 not o.total>100.
One clause per line. SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT — each on its own line at the base indentation level.
Use the SQL Formatter to automatically apply consistent formatting to any query, then adjust the output to match your team's conventions.