SQL Formatting Standards: Write Queries Humans Can Actually Read
SQL is one of the few languages where formatting is almost entirely up to the developer. There is no official style guide, no built-in formatter in most database clients, and no compiler that complains about indentation. The result is that most production SQL looks like it was written in a hurry at 2 AM — because it was.
Good formatting does not make SQL run faster. It makes SQL easier to review, debug, and modify. When a query is 200 lines long and joins six tables, formatting is the difference between understanding it in 30 seconds and staring at it for 10 minutes.
Uppercase keywords
The most widely adopted convention is to write SQL keywords in uppercase:
SELECT
u.id,
u.name,
u.email,
COUNT(o.id) AS order_count
FROM users u
JOIN orders o ON o.user_id = u.id
WHERE u.is_active = 1
GROUP BY u.id, u.name, u.email
HAVING COUNT(o.id) > 5
ORDER BY order_count DESC
LIMIT 20;
Uppercase keywords create a visual separation between the structural elements (SELECT, FROM, WHERE) and the data elements (column names, table names). Your eye can scan the left edge and immediately see the query structure.
Some teams prefer lowercase keywords. What matters is consistency — pick one and enforce it.
Indentation strategies
There are two common approaches:
Left-aligned keywords
Keywords sit on the left margin, and their arguments are indented:
SELECT
p.name,
p.price,
c.name AS category
FROM
products p
INNER JOIN
categories c ON c.id = p.category_id
WHERE
p.price > 1000
AND p.is_available = 1;
Right-aligned keywords
Keywords are right-aligned to a fixed column, so all arguments start at the same position:
SELECT p.name,
p.price,
c.name AS category
FROM products p
JOIN categories c ON c.id = p.category_id
WHERE p.price > 1000
AND p.is_available = 1;
Left-aligned is more common and easier to maintain. Right-aligned looks elegant but requires adjusting alignment when keywords change.
CTE formatting
Common Table Expressions (CTEs) are the building blocks of modern SQL. Format them as independent, readable units:
WITH monthly_sales AS (
SELECT
DATE_TRUNC('month', created_at) AS month,
SUM(total) AS revenue,
COUNT(*) AS order_count
FROM orders
WHERE status = 'completed'
GROUP BY DATE_TRUNC('month', created_at)
),
monthly_targets AS (
SELECT
month,
target_revenue
FROM sales_targets
)
SELECT
ms.month,
ms.revenue,
mt.target_revenue,
ms.revenue - mt.target_revenue AS variance
FROM monthly_sales ms
LEFT JOIN monthly_targets mt ON mt.month = ms.month
ORDER BY ms.month DESC;
Key conventions for CTEs:
- Each CTE gets a descriptive name (not
t1,t2) - The
WITHkeyword is on its own line or with the first CTE - Subsequent CTEs are separated by a comma on its own line or at the end of the closing parenthesis
- The final SELECT is clearly separated from the CTEs
JOIN alignment
Joins are where readability breaks down fastest. Keep the join type, table, and condition visually organized:
FROM orders o
INNER JOIN users u ON u.id = o.user_id
LEFT JOIN addresses a ON a.user_id = u.id AND a.is_primary = 1
LEFT JOIN coupons c ON c.id = o.coupon_id
For complex join conditions, break them across lines:
FROM orders o
LEFT JOIN shipments s
ON s.order_id = o.id
AND s.status = 'delivered'
AND s.delivered_at > o.created_at
Subquery formatting
Indent subqueries one level deeper than their surrounding context:
SELECT
u.name,
u.email
FROM users u
WHERE u.id IN (
SELECT DISTINCT o.user_id
FROM orders o
WHERE o.total > 10000
AND o.created_at > '2026-01-01'
);
If a subquery is complex enough to be hard to read inline, extract it into a CTE instead.
CASE expressions
Align WHEN, THEN, and ELSE for readability:
SELECT
name,
price,
CASE
WHEN price > 10000 THEN 'premium'
WHEN price > 5000 THEN 'standard'
ELSE 'budget'
END AS tier
FROM products;
Commas: leading vs trailing
Trailing commas (the default in most languages) put the comma at the end:
SELECT
id,
name,
email,
created_at
Leading commas put the comma at the start of each line:
SELECT
id
, name
, email
, created_at
Leading commas make it easier to comment out a line without breaking the query. Trailing commas are more familiar to most developers. Both are valid — pick one and stick with it.
Practical tips
- One column per line in SELECT. Horizontal lists of columns are hard to diff and review.
- Alias every table. Even if there is only one table, aliases keep queries consistent.
- Use meaningful aliases.
ufor users is fine.xfor users is not. - Break long WHERE clauses. One condition per line, with
AND/ORat the start of each line. - Comment complex logic. A two-line comment above a tricky CTE saves the next developer 20 minutes.
Try our SQL Formatter to reformat your queries instantly — right in your browser, no upload required.