SQL Style Guide: Formatting Best Practices
An opinionated guide to writing clean, consistent, and maintainable SQL. Covers the conventions that matter most in team environments.
Why SQL Style Matters
SQL is one of the few languages that most developers encounter regardless of their primary stack. Backend engineers, data analysts, DevOps teams, and even frontend developers writing database migrations all interact with SQL on a regular basis. Despite this ubiquity, SQL is rarely given the same attention to formatting and style that languages like Python, Go, or Rust receive. The result is codebases where every developer writes SQL in a slightly different way, making queries harder to read, review, and debug.
A shared SQL style guide eliminates ambiguity. When every query in your codebase follows the same conventions, code reviews become faster because reviewers spend less time parsing structure and more time evaluating logic. New team members can read existing queries immediately. Merge conflicts in SQL migration files decrease because formatting is predictable. Most importantly, well-formatted SQL makes logical errors visible: a misplaced JOIN condition or a missing WHERE clause stands out when the query structure is consistent and clear.
This guide is opinionated. Where there are legitimate alternatives (such as leading versus trailing commas), we present both options, explain the trade-offs, and recommend one. Use our SQL Formatter tool to automatically apply these conventions to your queries.
Keyword Casing
The most visible style choice in SQL is whether keywords are uppercase, lowercase, or mixed case. SQL itself is case-insensitive for keywords, so SELECT, select, and Select are all identical to the database engine. The choice is purely about human readability.
Uppercase Keywords (Recommended)
The most widely adopted convention is to write all SQL keywords in uppercase: SELECT, FROM, WHERE, JOIN, INSERT INTO, ORDER BY, and so on. This creates a strong visual contrast between the structural keywords of the query and the table names, column names, and values, which are typically written in lowercase or snake_case.
SELECT
u.id,
u.name,
u.email,
COUNT(o.id) AS order_count
FROM users u
INNER JOIN orders o
ON u.id = o.user_id
WHERE u.active = true
GROUP BY u.id, u.name, u.email
HAVING COUNT(o.id) > 5
ORDER BY order_count DESC
LIMIT 20;This convention is recommended by the majority of published SQL style guides, including those from GitLab, Mozilla, and Simon Holywell's widely-referenced sqlstyle.guide. It is also the default output of most SQL formatters, including ours.
Lowercase Keywords
Some teams, particularly those working with ORMs or writing SQL in application code, prefer lowercase keywords. The argument is that lowercase is easier to type and consistent with the rest of the application code. Modern syntax highlighting in editors makes the structural distinction less important since keywords are colored differently regardless of case.
select
u.id,
u.name,
u.email
from users u
inner join orders o
on u.id = o.user_id
where u.active = true
order by u.name;Lowercase is a reasonable choice if your team consistently uses it. The most important principle is consistency within a codebase. Never mix uppercase and lowercase keywords in the same query or the same repository.
Indentation
Indentation communicates hierarchy. The goal is to make it immediately clear which parts of a query are subordinate to which clauses. There are two primary approaches.
Two-Space Indentation (Recommended)
Two spaces is the most common indentation width for SQL. It provides enough visual hierarchy without consuming excessive horizontal space, which matters when queries contain deeply nested subqueries or CASE expressions. Column lists, JOIN conditions, and WHERE sub-expressions are all indented two spaces from their parent clause.
SELECT
u.id,
u.name,
CASE
WHEN u.role = 'admin' THEN 'Administrator'
WHEN u.role = 'mod' THEN 'Moderator'
ELSE 'User'
END AS role_label
FROM users u
WHERE u.active = true
AND u.created_at > '2025-01-01';Four-Space Indentation
Four spaces provides more visual clarity for deeply nested queries, but can push long lines past comfortable reading widths. Some teams use four spaces to match their application code conventions.
Tab Indentation
Tabs allow each developer to configure their preferred visual width, but can cause alignment issues in code reviews and diff tools. For SQL specifically, spaces are generally preferred because alignment of columns in SELECT lists and VALUES clauses is common and depends on consistent character widths.
Comma Placement
The placement of commas in column lists is one of the most debated topics in SQL formatting. There are two camps: trailing commas (the conventional approach) and leading commas (the pragmatic approach).
Trailing Commas (Conventional)
SELECT
u.id,
u.name,
u.email,
u.created_at
FROM users u;Trailing commas follow the same convention as most programming languages. The comma appears at the end of each line except the last. This reads naturally and is what most developers expect.
Leading Commas (Pragmatic)
SELECT
u.id
, u.name
, u.email
, u.created_at
FROM users u;Leading commas place the comma at the beginning of each line after the first. The advantage is purely practical: when you add or remove a column, the diff is always a single line. With trailing commas, adding a column to the end of the list requires modifying two lines (adding a comma to the previous last line and adding the new line). Leading commas also make it trivially easy to comment out a column by prefixing the line with --.
Our recommendation: use trailing commas unless your team has an existing leading-comma convention. Trailing commas are more widely adopted and familiar, and the diff advantage of leading commas is minor with modern code review tools.
JOIN Formatting
JOINs are among the most complex structural elements in SQL. Good formatting makes it immediately clear which tables are being joined and on what conditions.
Recommended JOIN Style
SELECT
u.id,
u.name,
o.id AS order_id,
o.total
FROM users u
INNER JOIN orders o
ON u.id = o.user_id
LEFT JOIN addresses a
ON u.id = a.user_id
AND a.is_primary = true
WHERE o.status = 'completed';Key principles: always write the full JOIN type explicitly (INNER JOIN instead of just JOIN, LEFT OUTER JOIN or LEFT JOIN). Place the ON condition on the next line, indented two spaces. If there are multiple ON conditions, each subsequent condition goes on its own indented line with AND. This makes it easy to see at a glance which conditions belong to which JOIN. For a visual explanation of all JOIN types, see our SQL JOIN types guide.
Subquery Formatting
Subqueries should be indented to clearly show nesting. Each nested query is a self-contained unit that should be readable on its own.
SELECT
u.id,
u.name,
recent_orders.total_amount
FROM users u
INNER JOIN (
SELECT
user_id,
SUM(total) AS total_amount
FROM orders
WHERE created_at > '2025-01-01'
GROUP BY user_id
) recent_orders
ON u.id = recent_orders.user_id
WHERE recent_orders.total_amount > 1000
ORDER BY recent_orders.total_amount DESC;For CTEs (Common Table Expressions), use a similar indentation pattern. CTEs are generally preferred over inline subqueries because they give the subquery a meaningful name and keep the main query readable.
WITH recent_orders AS (
SELECT
user_id,
SUM(total) AS total_amount
FROM orders
WHERE created_at > '2025-01-01'
GROUP BY user_id
)
SELECT
u.id,
u.name,
ro.total_amount
FROM users u
INNER JOIN recent_orders ro
ON u.id = ro.user_id
WHERE ro.total_amount > 1000
ORDER BY ro.total_amount DESC;Alias Conventions
Table and column aliases improve readability when used well, and harm it when used poorly. Follow these guidelines:
- Table aliases: Use short, meaningful abbreviations.
uforusers,ofororders,pforproducts. Avoid single-letter aliases that are ambiguous (e.g., do not useafor bothaccountsandaddressesin the same query). - Column aliases: Use the
ASkeyword explicitly. WriteCOUNT(o.id) AS order_count, notCOUNT(o.id) order_count. The explicitASmakes it clear that you are aliasing, not accidentally placing two expressions next to each other. - Avoid aliasing unless necessary: If a query references only one table, aliases add noise. Aliases become essential when a query involves multiple tables or when a computed column needs a meaningful name.
Comment Styles
SQL supports two comment syntaxes: single-line comments with -- and block comments with /* */. Use them consistently:
-- Single-line comment: explain the "why" of a specific line
SELECT
u.id,
u.name,
-- Include email only for active users (GDPR compliance)
CASE WHEN u.active THEN u.email ELSE NULL END AS email
FROM users u;
/*
* Block comment: describe the purpose of a complex query.
* This query calculates monthly revenue per product category
* for the trailing 12-month period, used by the finance dashboard.
*/
SELECT
p.category,
DATE_TRUNC('month', o.created_at) AS month,
SUM(o.total) AS revenue
FROM orders o
INNER JOIN products p
ON o.product_id = p.id
WHERE o.created_at >= CURRENT_DATE - INTERVAL '12 months'
GROUP BY p.category, DATE_TRUNC('month', o.created_at)
ORDER BY month DESC, revenue DESC;WHERE Clause Formatting
Complex WHERE clauses benefit from consistent formatting that reveals the logical structure:
WHERE u.active = true
AND u.role IN ('admin', 'moderator')
AND (
u.last_login > '2025-06-01'
OR u.created_at > '2025-01-01'
)
AND u.email IS NOT NULL;Each condition starts on its own line with AND or OR at the beginning (not the end). Parenthesized groups are indented to show logical grouping. This structure makes it immediately clear which conditions are ANDed together and which are ORed, reducing logic errors.
SELECT * Considered Harmful
In production queries, always list columns explicitly instead of using SELECT *. Explicit column lists serve as documentation, prevent unexpected breakage when table schemas change, reduce network transfer by excluding unneeded columns, and make it possible for the database query planner to use covering indexes.
SELECT * is acceptable in ad-hoc exploration queries, in subqueries where you immediately filter (e.g., EXISTS (SELECT * FROM ...)), and in development or debugging sessions. It should never appear in application code, migration scripts, or views.
Putting It All Together
Here is a complete example demonstrating all the conventions in this guide:
/*
* Monthly active users report
* Used by the analytics dashboard. Counts distinct users
* who placed at least one order in each calendar month.
*/
WITH monthly_orders AS (
SELECT
DATE_TRUNC('month', o.created_at) AS order_month,
o.user_id,
COUNT(*) AS orders_placed,
SUM(o.total) AS total_spent
FROM orders o
WHERE o.status IN ('completed', 'shipped')
AND o.created_at >= CURRENT_DATE - INTERVAL '6 months'
GROUP BY DATE_TRUNC('month', o.created_at), o.user_id
)
SELECT
mo.order_month,
COUNT(DISTINCT mo.user_id) AS active_users,
SUM(mo.orders_placed) AS total_orders,
ROUND(AVG(mo.total_spent), 2) AS avg_spend_per_user
FROM monthly_orders mo
INNER JOIN users u
ON mo.user_id = u.id
WHERE u.role != 'test'
AND u.active = true
GROUP BY mo.order_month
ORDER BY mo.order_month DESC;Consistent SQL formatting is a team discipline, not a personal preference. Pick a style, document it, enforce it with automated formatting, and spend your code review time on logic instead of style. Try formatting your own queries with our SQL Formatter to see these conventions applied automatically.
Further Reading
- sqlstyle.guide
Simon Holywell's SQL style guide based on Joe Celko's conventions.
- GitLab SQL Style Guide
GitLab's internal SQL style guide used for their data platform.
- PostgreSQL SQL Commands
Complete PostgreSQL SQL command reference for syntax verification.