The Complete Guide to SQL Query Formatting

Database schema diagram

It's 11 PM. You've been debugging a SQL query for two hours. The query is 200 lines long, all on one line, with table aliases like a1, a2, a3 that you're pretty sure stand for something meaningful but you can't remember what. Sound familiar?

I've been there. More importantly, I've been the person who wrote that query. And I've learned that good SQL formatting isn't about being neat—it's about survival.

Why Formatting Matters More Than You Think

SQL is unique among programming languages in that we read it far more than we write it. A SELECT statement might be written once but executed thousands of times, reviewed by multiple developers, and debugged at 2 AM when something breaks in production.

I've seen queries that looked correct but had subtle bugs invisible until you formatted them properly. I've also seen queries that looked like disasters but were actually correct—the formatting just made them look worse than they were.

The Basic Principles

Capitalize Keywords

This is the single biggest thing you can do to improve readability. SQL keywords (SELECT, FROM, WHERE, JOIN, etc.) should be uppercase. Everything else—table names, column names, aliases—can be lowercase. This creates visual hierarchy.

# Hard to parse
select u.name, o.total, o.date from users u join orders o on u.id = o.user_id where o.total > 100

# Easy to parse
SELECT u.name, o.total, o.date
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.total > 100

One Clause Per Line

Don't cram everything onto one line. Each major clause gets its own line. Within clauses, each condition gets its own line if it won't fit cleanly.

Align Similar Constructs

If you have multiple columns in a SELECT, align them. If you have multiple conditions in a WHERE clause, put them on separate lines and use indentation.

SELECT 
    u.id,
    u.name,
    u.email,
    u.created_at,
    o.order_count,
    o.total_spent
FROM users u
LEFT JOIN (
    SELECT user_id, COUNT(*) as order_count, SUM(total) as total_spent
    FROM orders
    GROUP BY user_id
) o ON u.id = o.user_id
WHERE u.status = 'active'
    AND u.created_at > '2023-01-01'
    AND o.total_spent > 100

JOIN Best Practices

JOINs are often where queries get messy. Here's my approach: put the JOIN keyword at the beginning of the line, indent the ON condition, and keep all the joining logic visually separated from the WHERE conditions.

Subqueries and CTEs

Modern SQL relies heavily on subqueries and Common Table Expressions (CTEs). CTEs are almost always preferable—they're more readable and easier to debug. Format them with the WITH keyword at the top, each CTE on its own line.

WITH active_users AS (
    SELECT id, name, email
    FROM users
    WHERE status = 'active'
),
user_orders AS (
    SELECT user_id, SUM(total) as total, COUNT(*) as order_count
    FROM orders
    GROUP BY user_id
)
SELECT 
    au.name,
    au.email,
    uo.total,
    uo.order_count
FROM active_users au
JOIN user_orders uo ON au.id = uo.user_id
WHERE uo.total > 100
ORDER BY uo.total DESC

Common Mistakes

Related Tools

SQL Formatter

Beautify and format SQL queries.

Related Articles

Writing Clean SQL

The art of readable SQL.