SQL has no enforced formatting standard. A query can be written as one dense line or spread across a hundred readable lines and the database does not care. But the people who read, review, debug, and maintain that query very much do. Consistent formatting is not aesthetic preference — it directly affects how quickly problems are found and how confidently changes are made.

Format SQL instantly Paste any SQL to get it formatted with consistent indentation, keyword capitalisation, and clause alignment.
Open SQL Formatter →

Capitalise Keywords, Lowercase Identifiers

The single most impactful formatting rule: SQL keywords in uppercase, everything else in lowercase. This creates an immediate visual distinction between the language syntax and your data model.

Avoid
select u.id, u.name, o.total from users u inner join orders o on u.id = o.user_id where o.total > 100
Prefer
SELECT u.id, u.name, o.total FROM users u INNER JOIN orders o ON u.id = o.user_id WHERE o.total > 100

How major SQL style guides compare

There is no universal SQL formatting standard. The major public style guides agree on more than they disagree on, but every rule has a counterexample somewhere. The table below shows where four widely-cited guides converge and where they diverge.

Rule GitLab Data dbt Labs Mozilla SQLFluff (default)
Keyword caselowercaselowercaseUPPERCASEUPPERCASE
Identifier casesnake_casesnake_casesnake_casesnake_case
Indentation4 spaces4 spaces4 spaces4 spaces
Comma styletrailingtrailingtrailingtrailing
JOIN typeexplicitexplicitexplicitexplicit
CTEs vs subqueriesCTEs preferredCTEs preferredeithernot enforced
Line length cap—~80 chars—120 chars
Trailing semicolonrequiredoptionalrequiredconfigurable

The lowercase-keywords convention from GitLab Data and dbt Labs is the loudest contemporary dissent against the long-standing UPPERCASE-keyword tradition. Their argument: lowercase reads more like prose, and modern editors handle the visual distinction with syntax highlighting. The counter-argument: in code review tools, email, terminal output, and plain-text diffs, syntax highlighting is not there — and UPPERCASE keywords still help readers parse intent. Pick one position, document it in your repo, and let a linter (SQLFluff being the most popular) enforce it.

One Clause Per Line

Each major clause — SELECT, FROM, JOIN, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT — starts on its own line. This makes it immediately obvious which clauses are present and makes adding or removing a clause a single-line change rather than a surgery on a long string.

Indent SELECT Columns

When selecting multiple columns, put each column on its own line indented under SELECT. This makes it easy to add, remove, or comment out a single column without touching other lines.

Prefer
SELECT u.id, u.email, u.created_at, o.total AS order_total FROM users u INNER JOIN orders o ON u.id = o.user_id

Indent ON Conditions Under JOIN

The ON condition belongs to the JOIN that precedes it, not to the WHERE clause. Indent it under the JOIN line to make this relationship clear, especially when there are multiple joins.

SELECT u.id, o.id, p.name FROM users u INNER JOIN orders o ON u.id = o.user_id INNER JOIN products p ON o.product_id = p.id WHERE o.status = 'completed'

Use Meaningful Table Aliases

Single-letter aliases (u, o) are fine for short queries, but for complex queries with many joins, use aliases that reflect the table name more clearly. The rule: an alias should make the query easier to read, not shorter to type.

Harder to read
SELECT a.id, b.name, c.total FROM users a INNER JOIN accounts b ON a.account_id = b.id INNER JOIN invoices c ON b.id = c.account_id
Clearer
SELECT usr.id, acc.name, inv.total FROM users usr INNER JOIN accounts acc ON usr.account_id = acc.id INNER JOIN invoices inv ON acc.id = inv.account_id

Explicit JOIN Types

Always write the JOIN type explicitly. JOIN alone is an INNER JOIN, but writing it out removes any ambiguity. A future reader (including yourself) should not need to remember whether an unqualified JOIN is inner or outer in your codebase.

WHERE Clause Formatting

For multiple conditions in a WHERE clause, put each condition on its own line with the boolean operator at the start of the line. This makes it easy to add, remove, or comment out a condition.

WHERE o.status = 'active' AND o.total > 100 AND o.created_at >= '2024-01-01'

Subquery Indentation

Subqueries should be indented consistently — the inner query should be indented relative to the outer query's clause it belongs to. Always alias a subquery.

SELECT u.id, recent.total FROM users u INNER JOIN ( SELECT user_id, SUM(total) AS total FROM orders WHERE created_at >= NOW() - INTERVAL '30 days' GROUP BY user_id ) AS recent ON u.id = recent.user_id
Don't apply these by hand Paste any SQL into the free SQL Formatter and every rule above is applied in one click — keyword case, indentation, JOIN alignment.
Open SQL Formatter →

Avoid SELECT *

SELECT * in production code is almost always wrong. It returns columns you do not need (wasting bandwidth), breaks when the table schema changes, and makes it impossible to know what data the query returns without looking at the table definition. Always name the columns you need explicitly.


Frequently Asked Questions

Does SQL formatting affect query performance?
No — the database parser ignores whitespace completely. Formatting is purely for human readability. The query engine produces the same execution plan regardless of how the SQL is formatted. The only performance-relevant decisions are in the logic of the query itself, not its visual presentation.
Should SQL keywords be uppercase or lowercase?
Uppercase keywords is the strong industry convention, though SQL itself is case-insensitive for keywords. The reason is purely readability — uppercase keywords create an immediate visual distinction between the SQL syntax and your identifiers. Most linters, code review tools, and SQL style guides (SQLFluff, dbt style guide, GitLab SQL style guide) default to uppercase keywords.
Should I use leading or trailing commas for SELECT columns?
Both are in common use. Trailing commas (comma at the end of each line) match most programming languages and feel natural. Leading commas (comma at the start of each line) make it easier to comment out the last column without adjusting the line above. The most important rule is to be consistent within a codebase. If your team or ORM generates trailing commas, use trailing commas.
What is the difference between WHERE and HAVING?
WHERE filters rows before aggregation — it operates on individual rows and cannot use aggregate functions. HAVING filters after aggregation — it operates on grouped results and can use aggregate functions like SUM(), COUNT(), AVG(). A common pattern: WHERE to pre-filter rows for performance, HAVING to filter aggregated groups: WHERE status = 'active' GROUP BY user_id HAVING COUNT(*) > 5.
Should I use CTEs (WITH clauses) or subqueries?
For anything beyond a single inline subquery, prefer a CTE (Common Table Expression) using the WITH clause. CTEs are named, reusable, and the query reads top-to-bottom in the same order the work happens. Subqueries are fine for one-line filters in a WHERE or FROM, but multi-line subqueries embedded mid-query are harder to follow than the same logic broken out as a CTE. Performance is essentially identical on every modern database — Postgres, MySQL 8+, SQL Server, BigQuery, and Snowflake all inline CTEs unless explicitly materialised.
What is the dbt SQL style guide?
The dbt Labs SQL style guide is the de-facto standard for analytics SQL in modern data teams. Its main positions are: lowercase keywords (a break from the older UPPERCASE tradition), snake_case identifiers, trailing commas, four-space indentation, explicit INNER JOIN over bare JOIN, and a strong preference for CTEs over nested subqueries. It is the basis for most dbt-templated project styles and is open source on GitHub.
How long should a SQL query be?
There is no hard limit, but once a query exceeds roughly 30 lines, break it into CTEs. Each CTE should do one thing and carry a name that says what it does — active_users, orders_last_30_days. A 200-line monolithic query with seven joins and three subqueries is correct but unmaintainable; the same logic as five named CTEs is reviewable in five chunks. Aim for line length under 120 characters so the query fits in a side-by-side diff without horizontal scrolling.