Databases·6 min read·By the StackUtils Team

SQL Formatting Best Practices: Write SQL That Others Can Read

Unformatted SQL is a maintenance nightmare. This guide covers indentation conventions, JOIN alignment, CTE style, and how consistent formatting prevents logic bugs in complex queries.

Why SQL Formatting Matters

SQL has no enforced style. A query can be one line or one hundred, with keywords in any case, columns anywhere. This freedom creates two problems in real teams:

  • Logic bugs hidden by poor formatting — a misplaced JOIN condition looks identical to a WHERE filter when everything is on one line.
  • Review bottleneck — reviewers spend cognitive energy parsing layout instead of evaluating logic.

Consistent formatting is cheap to adopt and permanently solves both problems. Here is the style that works across dialects and teams.

Before and After

❌ Unformatted

select u.id,u.name,o.total,p.name as product from users u join orders o on u.id=o.user_id join order_items oi on o.id=oi.order_id join products p on oi.product_id=p.id where u.created_at > '2025-01-01' and o.status='completed' order by o.total desc limit 20

✅ Formatted

SELECT
    u.id,
    u.name,
    o.total,
    p.name  AS product
FROM users              AS u
JOIN orders             AS o  ON u.id = o.user_id
JOIN order_items        AS oi ON o.id = oi.order_id
JOIN products           AS p  ON oi.product_id = p.id
WHERE
    u.created_at > '2025-01-01'
    AND o.status = 'completed'
ORDER BY o.total DESC
LIMIT 20;

8 Formatting Rules That Work

  1. Uppercase SQL keywords. SELECT, FROM, WHERE, JOIN — always uppercase. Column names and aliases — lowercase. This visual contrast makes structure instantly clear.
  2. One clause per line. Each top-level keyword (SELECT, FROM, WHERE, GROUP BY, ORDER BY) starts on its own line.
  3. Indent column lists. Columns under SELECT are indented 4 spaces. Put the comma at the start of each line (leading comma) — makes it trivial to comment out any single column.
  4. Align JOIN conditions. The ON keyword aligns vertically with other ON keywords. This makes it obvious at a glance which table each JOIN connects to.
  5. AND / OR at the start of a line. In WHERE and HAVING, start each condition with AND / OR at the beginning of a new line, indented.
  6. Always use table aliases. Even for single-table queries, an alias (u for users) makes column ownership clear and simplifies future JOINs.
  7. Name subqueries and CTEs meaningfully. WITH recent_orders AS (...) beats WITH t1 AS (...) every time.
  8. End every statement with a semicolon. Required in some contexts, harmless in all — and makes multi-statement files unambiguous.

CTE Formatting Pattern

Common Table Expressions are powerful but easily become unreadable. Follow this layout:

WITH
active_users AS (
    SELECT id, name, email
    FROM   users
    WHERE  is_active = TRUE
),
recent_orders AS (
    SELECT user_id, COUNT(*) AS order_count, SUM(total) AS revenue
    FROM   orders
    WHERE  created_at >= CURRENT_DATE - INTERVAL '30 days'
    GROUP BY user_id
)
SELECT
    u.name,
    u.email,
    COALESCE(ro.order_count, 0) AS orders_last_30d,
    COALESCE(ro.revenue,     0) AS revenue_last_30d
FROM active_users       AS u
LEFT JOIN recent_orders AS ro ON u.id = ro.user_id
ORDER BY ro.revenue DESC NULLS LAST;

Mistakes That Cause Bugs

SELECT * FROM orders JOIN users ON orders.id = users.id

Joining on the wrong column — orders.id should be orders.user_id. Easy to miss on one line.

WHERE status = 'active' AND created_at > '2025-01-01' OR is_vip = TRUE

Operator precedence: AND binds tighter than OR. This is (status AND date) OR vip — probably not the intent. Use parentheses.

SELECT name, COUNT(*) FROM users GROUP BY id

Selecting a non-aggregated, non-grouped column. Silently returns arbitrary values in MySQL.

Format any SQL query instantly

Paste messy SQL to reformat it with consistent indentation and keyword casing — supports MySQL, PostgreSQL, SQLite, and more.

Open SQL Formatter →