SQL Formatting Best Practices: Write Readable Queries Every Time
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` LIMITclause for pagination:LIMIT 10 OFFSET 20INSERT ... SETsyntax as alternative toVALUES
SELECT
`id`,
`name`
FROM `users`
WHERE `active` = 1
LIMIT 10 OFFSET 20;
PostgreSQL
- Double-quoted identifiers:
"ColumnName" LIMIT/OFFSETorFETCH FIRST::typecast syntaxILIKEfor case-insensitive matching
SELECT
"id",
"name"
FROM "users"
WHERE "active" = true
LIMIT 10 OFFSET 20;
SQL Server (T-SQL)
- Square-bracket identifiers:
[ColumnName] TOPclause instead ofLIMITOFFSET ... FETCH NEXTfor 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/OFFSETsyntax like MySQL/PostgreSQL- No built-in
DATE_TRUNCorGENERATE_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 TABLEor 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:
| Element | Convention |
|---|---|
| Keywords | UPPERCASE |
| Identifiers | lowercase (or snake_case) |
| String literals | Single quotes |
| Column list | One per line, trailing comma |
| Clause keywords | New line, no indentation |
| Join conditions | Indented under JOIN |
| WHERE conditions | Indented, AND/OR on new line |
| Subqueries | Indented as a block |
| Operators | Spaces on both sides |
| Semicolons | At 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.