SQL Formatting Best Practices: Write Readable Queries Every Time

CodeKit
sqlformattingdatabase

Why SQL Formatting Matters

SQL is a declarative language—you describe what you want, not how to get it. This makes SQL concise, but it also makes unformatted SQL deceptively hard to read. Consider this real-world query:

SELECT u.id,u.name,u.email,o.total FROM users u JOIN orders o ON u.id=o.user_id WHERE o.created_at>'2025-01-01' AND o.total>100 ORDER BY o.total DESC;

Now the same query, formatted:

SELECT
    u.id,
    u.name,
    u.email,
    o.total
FROM users u
JOIN orders o
    ON u.id = o.user_id
WHERE o.created_at > '2025-01-01'
    AND o.total > 100
ORDER BY o.total DESC;

The formatted version is immediately clearer. You can see which columns are selected, how tables are joined, and what the filter conditions are—without mentally parsing a wall of text.

Good formatting isn’t about aesthetics. It’s about:

  • Reducing bugs: Misread conditions lead to wrong results
  • Faster code review: Reviewers spend less time parsing and more time evaluating logic
  • Easier maintenance: You’ll revisit this query months from now and need to understand it quickly
  • Team consistency: A shared style eliminates formatting debates in pull requests

You can format SQL automatically with the SQL Formatter on CodeKit.

Core Formatting Principles

1. Uppercase Keywords

SQL keywords should be uppercase to visually distinguish them from identifiers and values:

-- Good
SELECT id, name FROM users WHERE active = true;

-- Bad
select id, name from users where active = true;

Uppercase keywords create a clear visual rhythm. Your eye naturally jumps to the keywords, making the query’s structure apparent at a glance.

2. One Column per Line in SELECT

When selecting multiple columns, put each on its own line:

SELECT
    id,
    first_name,
    last_name,
    email,
    created_at
FROM users;

This makes it easy to add, remove, or reorder columns without touching other lines. It also produces cleaner diffs in version control—each column change is a single line.

3. Indent FROM, WHERE, and Other Clauses

Start each major clause on a new line at the same indentation level:

SELECT
    id,
    name
FROM users
WHERE active = true
ORDER BY name;

4. Indent Join Conditions

Place ON conditions on a new line, indented under the JOIN:

SELECT
    u.id,
    u.name,
    o.total
FROM users u
JOIN orders o
    ON u.id = o.user_id
LEFT JOIN payments p
    ON o.id = p.order_id
WHERE o.total > 100;

5. Indent Nested Conditions

When a WHERE clause has multiple conditions connected by AND/OR, indent each condition:

WHERE o.created_at >= '2025-01-01'
    AND o.created_at < '2025-02-01'
    AND o.status = 'completed'
    AND o.total > 100

6. Spaces Around Operators

Always add spaces around =, >, <, >=, <=, <>, and !=:

-- Good
WHERE age >= 18 AND status = 'active'

-- Bad
WHERE age>=18 AND status='active'

7. Trailing Commas

Prefer trailing commas (comma after each item) over leading commas. While leading commas make it easy to comment out individual lines, trailing commas are more natural to read and are supported by most SQL dialects:

-- Trailing commas (preferred)
SELECT
    id,
    name,
    email,
FROM users;

-- Leading commas (alternative)
SELECT
    id
    , name
    , email
FROM users;

Formatting Complex Queries

Subqueries

Format subqueries with consistent indentation, treating them like nested blocks:

SELECT
    u.id,
    u.name,
    recent.total
FROM users u
JOIN (
    SELECT
        user_id,
        SUM(total) AS total
    FROM orders
    WHERE created_at >= '2025-01-01'
    GROUP BY user_id
) recent
    ON u.id = recent.user_id
WHERE u.active = true;

CTEs (Common Table Expressions)

CTEs are often easier to read than subqueries. Format them with the AS on the same line as the CTE name:

WITH recent_orders AS (
    SELECT
        user_id,
        SUM(total) AS total
    FROM orders
    WHERE created_at >= '2025-01-01'
    GROUP BY user_id
),
user_stats AS (
    SELECT
        id,
        name,
        active
    FROM users
    WHERE active = true
)
SELECT
    s.id,
    s.name,
    r.total
FROM user_stats s
JOIN recent_orders r
    ON s.id = r.user_id
ORDER BY r.total DESC;

CASE Expressions

Format CASE expressions with each WHEN on its own line:

SELECT
    id,
    CASE
        WHEN total >= 1000 THEN 'high'
        WHEN total >= 100 THEN 'medium'
        ELSE 'low'
    END AS priority
FROM orders;

Window Functions

Window function clauses (OVER, PARTITION BY, ORDER BY) should be indented clearly:

SELECT
    id,
    name,
    department,
    salary,
    RANK() OVER (
        PARTITION BY department
        ORDER BY salary DESC
    ) AS dept_rank
FROM employees;

Dialect Differences

SQL dialects have syntax differences that affect formatting:

MySQL

  • Backtick-quoted identifiers: `column_name`
  • LIMIT clause for pagination: LIMIT 10 OFFSET 20
  • INSERT ... SET syntax as alternative to VALUES
SELECT
    `id`,
    `name`
FROM `users`
WHERE `active` = 1
LIMIT 10 OFFSET 20;

PostgreSQL

  • Double-quoted identifiers: "ColumnName"
  • LIMIT/OFFSET or FETCH FIRST
  • ::type cast syntax
  • ILIKE for case-insensitive matching
SELECT
    "id",
    "name"
FROM "users"
WHERE "active" = true
LIMIT 10 OFFSET 20;

SQL Server (T-SQL)

  • Square-bracket identifiers: [ColumnName]
  • TOP clause instead of LIMIT
  • OFFSET ... FETCH NEXT for pagination
SELECT TOP 10
    [id],
    [name]
FROM [users]
WHERE [active] = 1
ORDER BY [id]
OFFSET 20 ROWS
FETCH NEXT 10 ROWS ONLY;

SQLite

  • Supports both backtick and double-quote identifiers
  • LIMIT/OFFSET syntax like MySQL/PostgreSQL
  • No built-in DATE_TRUNC or GENERATE_SERIES

When formatting SQL for a specific dialect, the formatter should use the correct identifier quoting style and respect dialect-specific syntax.

Auto-Formatting SQL

Manually formatting SQL is tedious and inconsistent. Auto-formatters solve both problems.

What SQL Formatters Do

A good SQL formatter handles:

  • Keyword casing: Converts keywords to uppercase (or lowercase, per your preference)
  • Indentation: Consistent indentation for clauses, subqueries, and conditions
  • Line breaks: Places each clause and column on its own line
  • Spacing: Adds spaces around operators and after commas
  • Identifier quoting: Applies dialect-appropriate quoting
  • Comment preservation: Keeps your comments intact and positioned correctly

When to Use a Formatter

  • Before committing: Format SQL as part of your pre-commit workflow
  • In code review: Reject unformatted SQL just like you’d reject unformatted code
  • When exploring data: Paste the output of SHOW CREATE TABLE or a query from logs into a formatter to make it readable
  • When editing legacy queries: Format before making changes so you can see the structure clearly

Formatting with the SQL Formatter

The SQL Formatter on CodeKit lets you paste any SQL statement, select your dialect (MySQL, PostgreSQL, SQL Server, SQLite, and more), and get clean, consistently formatted output instantly. It handles complex queries with CTEs, subqueries, window functions, and CASE expressions—all in your browser with no data sent to a server.

Style Guide Summary

Here’s a quick reference for a consistent SQL style:

ElementConvention
KeywordsUPPERCASE
Identifierslowercase (or snake_case)
String literalsSingle quotes
Column listOne per line, trailing comma
Clause keywordsNew line, no indentation
Join conditionsIndented under JOIN
WHERE conditionsIndented, AND/OR on new line
SubqueriesIndented as a block
OperatorsSpaces on both sides
SemicolonsAt the end of the statement

Conclusion

SQL formatting is not a luxury—it’s a professional discipline that reduces errors, accelerates code review, and makes maintenance painless. The key principles are simple: uppercase keywords, one item per line, consistent indentation, and spaces around operators. For complex queries with CTEs, subqueries, and window functions, these conventions become even more valuable.

Don’t format by hand. Use an auto-formatter to enforce consistency without thinking about it. Try the SQL Formatter on CodeKit—paste your query, pick your dialect, and get clean, readable SQL in seconds.