SQL Formatting Best Practices: Style Guide, Naming Conventions & Optimization

A comprehensive guide to writing clean, readable, and maintainable SQL code with industry-standard conventions.

Advertisement

Why SQL Formatting Matters

SQL is one of the most widely used programming languages in the world, powering everything from small applications to massive data warehouses processing petabytes of data. Yet SQL formatting is often overlooked — developers write queries as single-line strings, use inconsistent capitalization, and create deeply nested subqueries that are nearly impossible to debug.

Well-formatted SQL is not just about aesthetics. It directly impacts productivity, code quality, and system reliability. Studies show that developers spend 70% of their time reading code rather than writing it. When SQL queries are properly formatted, team members can quickly understand complex logic, identify potential issues during code reviews, and make modifications with confidence.

This guide covers the most widely accepted SQL formatting conventions, drawing from industry standards used at companies like Google, Facebook, and Airbnb. Whether you are writing simple SELECT statements or complex analytical queries with multiple CTEs and window functions, these practices will help you write SQL that is a pleasure to read and maintain.

Keyword Capitalization

The most universally accepted SQL convention is to write SQL keywords in UPPERCASE and identifiers (table names, column names) in lowercase:

✅ Good

SELECT
  u.first_name,
  u.email,
  COUNT(o.id) AS order_count
FROM users AS u
LEFT JOIN orders AS o
  ON u.id = o.user_id
WHERE u.status = 'active'
GROUP BY u.id
HAVING COUNT(o.id) > 5
ORDER BY order_count DESC
LIMIT 10;

❌ Bad

select u.first_name, u.email,
count(o.id) as order_count
from users u left join orders o
on u.id = o.user_id where
u.status = 'active' group by
u.id having count(o.id) > 5
order by order_count desc limit 10;

Indentation & Line Breaks

Proper indentation is the single most impactful formatting practice. Follow these rules:

  • Major clauses on new lines — SELECT, FROM, WHERE, JOIN, GROUP BY, HAVING, ORDER BY, LIMIT each start on a new line at the base indentation level
  • Column lists indented — Each column in SELECT gets its own line, indented 2-4 spaces
  • JOIN conditions indented — ON clauses are indented under their JOIN
  • WHERE conditions — Each AND/OR condition on a new line, with the operator at the start
  • Subqueries indented — Subqueries are indented one level from the enclosing query
SELECT
  p.product_name,
  p.category,
  p.price,
  SUM(oi.quantity) AS total_sold,
  SUM(oi.quantity * oi.unit_price) AS total_revenue
FROM products AS p
INNER JOIN order_items AS oi
  ON p.id = oi.product_id
INNER JOIN orders AS o
  ON oi.order_id = o.id
WHERE o.status = 'completed'
  AND o.created_at >= '2024-01-01'
  AND p.category IN ('electronics', 'clothing')
GROUP BY
  p.id,
  p.product_name,
  p.category,
  p.price
HAVING SUM(oi.quantity) > 10
ORDER BY total_revenue DESC
LIMIT 50;

Naming Conventions

Tables

  • Use plural snake_case: users, order_items, product_categories
  • Avoid prefixes like tbl_ or t_ — they add noise without value
  • Use descriptive names: user_login_history not ulh
  • Junction tables: user_roles, product_tags (both table names, alphabetical)

Columns

  • Use singular snake_case: first_name, created_at, is_active
  • Primary key: id (simple) or user_id (descriptive)
  • Foreign keys: match the referenced table: user_id references users.id
  • Booleans: prefix with is_, has_, can_: is_active, has_subscription
  • Timestamps: suffix with _at: created_at, updated_at, deleted_at
  • Dates: suffix with _date or _on: birth_date, expires_on

Indexes

  • Pattern: idx_tablename_columnname(s)
  • Unique: uq_tablename_columnname
  • Example: idx_users_email, idx_orders_user_id_created_at

Common Table Expressions (CTEs)

CTEs (WITH clauses) are one of the most powerful tools for writing readable SQL. They let you break complex queries into named, logical steps:

WITH monthly_revenue AS (
  SELECT
    DATE_TRUNC('month', o.created_at) AS month,
    SUM(o.total_amount) AS revenue
  FROM orders AS o
  WHERE o.status = 'completed'
  GROUP BY DATE_TRUNC('month', o.created_at)
),
revenue_growth AS (
  SELECT
    month,
    revenue,
    LAG(revenue) OVER (ORDER BY month) AS prev_revenue,
    ROUND(
      (revenue - LAG(revenue) OVER (ORDER BY month))
      / LAG(revenue) OVER (ORDER BY month) * 100,
      2
    ) AS growth_pct
  FROM monthly_revenue
)
SELECT
  month,
  revenue,
  prev_revenue,
  growth_pct
FROM revenue_growth
WHERE month >= '2024-01-01'
ORDER BY month;

CTEs make queries self-documenting. Each CTE has a descriptive name that explains what it computes, making the overall query logic clear even to someone seeing it for the first time.

Query Optimization Tips

  • Use EXPLAIN ANALYZE — Always check the execution plan before optimizing. Focus on the actual bottleneck, not assumptions.
  • Avoid SELECT * — Only select the columns you need. This reduces I/O, memory usage, and network transfer.
  • Index strategically — Create indexes on columns used in WHERE, JOIN, and ORDER BY clauses. But do not over-index — each index slows down writes.
  • Use appropriate JOINs — INNER JOIN when you need matching rows in both tables. LEFT JOIN when you need all rows from the left table. Avoid CROSS JOIN unless intentional.
  • Limit result sets — Always use LIMIT for queries that could return large result sets. Implement pagination for user-facing queries.
  • Avoid functions on indexed columnsWHERE YEAR(created_at) = 2024 cannot use an index. Use WHERE created_at >= '2024-01-01' instead.
  • Use EXISTS over IN for subqueries — EXISTS stops at the first match, while IN evaluates the entire subquery.
  • Batch large operations — Instead of updating millions of rows at once, process in batches of 1,000-10,000 to avoid lock contention.

SQL Anti-Patterns to Avoid

N+1 Queries

Running a query for each row in a loop

Use JOINs or batch queries to fetch related data in one query

Implicit Joins

FROM users, orders WHERE users.id = orders.user_id

Use explicit JOIN syntax: FROM users JOIN orders ON ...

SELECT * in Production

SELECT * FROM users (fetches all columns)

SELECT id, name, email FROM users (only needed columns)

Missing WHERE on UPDATE/DELETE

UPDATE users SET status = "inactive" (updates ALL rows!)

Always include WHERE clause; use transactions for safety

String Concatenation for Queries

"SELECT * FROM users WHERE id = " + userId

Use parameterized queries to prevent SQL injection

SQL Formatting Tools

Automate your SQL formatting with these tools:

  • Our SQL Formatter — Free online tool supporting MySQL, PostgreSQL, SQLite, and SQL Server dialects
  • SQLFluff — Python-based SQL linter and formatter with configurable rules
  • pgFormatter — PostgreSQL-specific formatter with extensive options
  • DBeaver — Database IDE with built-in SQL formatting
  • VS Code Extensions — SQL Formatter, SQLTools, and other extensions provide format-on-save

Format Your SQL Queries

Use our free SQL formatter to beautify your queries with proper indentation and keyword capitalization. Supports multiple SQL dialects.

Open SQL Formatter →
Advertisement