SQL Formatting Best Practices: Write Clean Queries

Uppercase Keywords

Always write SQL keywords in uppercase. This creates a clear visual distinction between the language structure and your identifiers.

-- Bad: keywords blend with column names
select u.name, u.email, o.total
from users u
inner join orders o on u.id = o.user_id
where u.active = 1 and o.total > 50

-- Good: keywords stand out
SELECT u.name, u.email, o.total
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE u.active = 1 AND o.total > 50

Indentation Rules

Each clause starts on a new line. Column lists are indented. Conditions in WHERE clauses are aligned or each on their own line for complex filters.

-- Bad: everything crammed together
SELECT u.name, o.total, p.title FROM users u JOIN orders o ON u.id = o.user_id JOIN products p ON o.product_id = p.id WHERE u.active = 1 AND o.total > 50 AND p.category = 'electronics'

-- Good: one clause per line, indented columns
SELECT
    u.name,
    o.total,
    p.title
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN products p ON o.product_id = p.id
WHERE u.active = 1
    AND o.total > 50
    AND p.category = 'electronics'

JOIN Formatting

Each JOIN on its own line with the join condition directly below. LEFT/RIGHT/FULL keywords make join types immediately visible.

SELECT
    u.name,
    o.id AS order_id,
    o.total,
    p.name AS product
FROM users u
LEFT JOIN orders o
    ON o.user_id = u.id
    AND o.status = 'completed'
INNER JOIN products p
    ON p.id = o.product_id
    AND p.active = 1
WHERE u.created_at > '2026-01-01'
ORDER BY o.total DESC
LIMIT 100;

Tip: When a JOIN has multiple conditions, put each on its own indented line with AND/OR at the start. This makes it easy to add or remove conditions without breaking the query.

CTE Formatting

Common Table Expressions (WITH clauses) improve readability for complex queries. Each CTE is a named, self-contained unit.

WITH active_users AS (
    SELECT
        id,
        name,
        email
    FROM users
    WHERE active = 1
      AND last_login > CURRENT_DATE - INTERVAL '30 days'
),
user_orders AS (
    SELECT
        user_id,
        COUNT(*) AS order_count,
        SUM(total) AS lifetime_value
    FROM orders
    WHERE status = 'completed'
    GROUP BY user_id
)
SELECT
    au.name,
    au.email,
    uo.order_count,
    uo.lifetime_value
FROM active_users au
JOIN user_orders uo ON uo.user_id = au.id
WHERE uo.order_count >= 3
ORDER BY uo.lifetime_value DESC;

Format your SQL queries automatically?

Try SQL Formatter