How to Format SQL Queries for Readability and Debugging
SQL doesn't care about whitespace — but your team does. Consistently formatted SQL is dramatically easier to understand during code review, faster to debug, and less prone to errors hidden by visual noise. This guide covers the widely adopted conventions.
Before and After: The Difference Formatting Makes
✗ Unformatted — hard to read at a glance
select u.id,u.name,u.email,count(o.id) as order_count,sum(o.total) as total_spent from users u left join orders o on u.id=o.user_id where u.created_at>'2024-01-01' and u.status='active' group by u.id,u.name,u.email having count(o.id)>0 order by total_spent desc limit 20
✓ Formatted — structure is immediately clear
SELECT
u.id,
u.name,
u.email,
COUNT(o.id) AS order_count,
SUM(o.total) AS total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE
u.created_at > '2024-01-01'
AND u.status = 'active'
GROUP BY
u.id, u.name, u.email
HAVING COUNT(o.id) > 0
ORDER BY total_spent DESC
LIMIT 20 Key Formatting Rules
Uppercase SQL keywords
SELECT, FROM, WHERE, JOIN, GROUP BY, ORDER BY, HAVING, LIMIT — keywords uppercase, identifiers lowercase. Makes the structure scannable at a glance.
One clause per line
Each major clause (SELECT, FROM, WHERE, JOIN) starts on its own line. This is the single biggest readability improvement.
Indent column lists
Columns in SELECT, conditions in WHERE, and GROUP BY columns are indented 4 spaces from the keyword.
Align AS aliases
When multiple aliased columns are in a row, align the AS keyword vertically for easy scanning.
Put AND/OR at line start
WHERE condition AND other_condition is clearer than condition AND other_condition — the operator at the start signals continuation.
Comma-first style (optional)
Some teams put commas at the start of the next line: , column_name. Makes it easy to comment out a column without touching the previous line. Controversial — pick one and be consistent.
Using CTEs for Complex Queries
Common Table Expressions (CTEs) break a complex query into named, readable steps — like functions for SQL:
WITH
active_users AS (
SELECT id, name, email
FROM users
WHERE status = 'active'
AND created_at > '2024-01-01'
),
user_orders AS (
SELECT
user_id,
COUNT(*) AS order_count,
SUM(total) AS total_spent
FROM orders
WHERE completed_at IS NOT NULL
GROUP BY user_id
)
SELECT
u.name,
u.email,
COALESCE(o.order_count, 0) AS order_count,
COALESCE(o.total_spent, 0) AS total_spent
FROM active_users u
LEFT JOIN user_orders o ON u.id = o.user_id
ORDER BY total_spent DESC Automated SQL Formatters
Open-source linter and formatter for SQL. Supports PostgreSQL, MySQL, BigQuery, Snowflake, and others. Integrates with CI/CD and pre-commit hooks.
Specifically for PostgreSQL. Excellent for complex queries with PG-specific syntax. Available as CLI, Perl module, and web tool.
JavaScript library with a simple API. Works in Node.js and browsers. Supports multiple SQL dialects.
DataGrip, VS Code (SQL Formatter extension), and DBeaver all have built-in SQL formatting. Configure them to match your team's style guide.
Format SQL queries instantly — free
SQL Formatter