Nobody disputes that code should be readable. But somehow, SQL gets a pass — compressed into a single line, aliases that are single letters, subqueries nested four levels deep. Here's why that's a mistake and what good SQL formatting actually looks like.
Why SQL Formatting Gets Ignored
A few reasons SQL readability gets neglected:
- It "works" either way. The database doesn't care about whitespace.
- ORMs hide the SQL. Developers who write Hibernate or SQLAlchemy rarely see raw SQL.
- No enforced style guide. Python has PEP 8; SQL has no universally adopted equivalent.
- Copy-paste culture. SQL often comes from Stack Overflow or query builders, pre-minified.
The cost shows up in code review ("wait, what does this actually do?"), debugging ("where does this subquery start?"), and onboarding ("who wrote this and can I call them?").
The Core Rules
Keywords in uppercase (or consistently lower)
Pick one and stick to it. Most style guides prefer uppercase keywords because it creates visual hierarchy:
-- Good
SELECT id, name, email
FROM users
WHERE created_at > '2026-01-01'
AND is_active = true
ORDER BY name ASC;
-- Acceptable (consistent lowercase)
select id, name, email
from users
where created_at > '2026-01-01'
and is_active = true
order by name asc;
One clause per line
Each major clause (SELECT, FROM, WHERE, JOIN, GROUP BY, ORDER BY) starts on its own line. Conditions within a clause are indented:
SELECT
u.id,
u.name,
COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o
ON o.user_id = u.id
WHERE u.is_active = true
AND u.created_at > '2026-01-01'
GROUP BY u.id, u.name
HAVING COUNT(o.id) > 0
ORDER BY order_count DESC
LIMIT 100;
Alias everything, but name it well
Single-letter aliases (u, o) are fine in short queries. In long queries, use descriptive aliases:
-- Short: fine
SELECT u.id FROM users u JOIN orders o ON o.user_id = u.id;
-- Long: be descriptive
SELECT
active_users.id,
recent_orders.total
FROM users AS active_users
JOIN orders AS recent_orders
ON recent_orders.user_id = active_users.id;
Subqueries: always indent
SELECT *
FROM (
SELECT
user_id,
SUM(amount) AS total_spent
FROM orders
WHERE status = 'completed'
GROUP BY user_id
) AS spending
WHERE spending.total_spent > 1000;
CTEs over nested subqueries
Common Table Expressions make complex logic readable:
WITH active_users AS (
SELECT id, name
FROM users
WHERE is_active = true
),
high_spenders AS (
SELECT user_id, SUM(amount) AS total
FROM orders
GROUP BY user_id
HAVING SUM(amount) > 1000
)
SELECT
au.name,
hs.total
FROM active_users au
JOIN high_spenders hs
ON hs.user_id = au.id
ORDER BY hs.total DESC;
Formatting Reference
| Element | Convention |
|---|---|
| Keywords | UPPERCASE |
| Table/column names | lowercase_snake_case |
| Aliases | AS keyword (explicit) |
| Commas | End of line (trailing) or start of line (leading) — pick one |
| Indentation | 4 spaces (not tabs) |
| Line length | Max 100 chars |
| Semicolons | Always close statements |
Tools That Help
Running raw SQL through a formatter before committing it takes about 5 seconds and saves everyone who reads it later 5 minutes.
SnappyTools SQL Formatter & Beautifier formats any SQL query in the browser — supports indentation, keyword casing, and basic dialect-aware formatting. Paste in the ugly version, copy out the clean version.
No account, no upload, runs locally in your browser.
Do you have a team SQL style guide? Or does every developer format differently?
Top comments (0)