SQL Formatting: Writing Readable, Maintainable Queries
Learn SQL formatting conventions, explicit joins, CTEs and parameterised queries to write readable, maintainable SQL that is easy to review.
.jpg)
Every codebase eventually collects a query that nobody wants to touch. It runs to eighty lines, the joins are tangled together, and the only person who understood it left last year. The logic may be fine. The layout is the problem. This guide shows how to format SQL so that queries are readable, reviewable and maintainable: the conventions that matter, the habits that cause trouble, and how to automate the tedious parts.
What is SQL, and why does formatting matter?
SQL, Structured Query Language, is the standard language for working with relational databases. It is declarative: you describe the result you want, and the database engine decides how to fetch it. The language is standardised by ISO and implemented, with differences, by PostgreSQL, MySQL, SQL Server, Oracle, SQLite and others. The PostgreSQL documentation is one of the clearest references for the syntax.
SQL ignores whitespace and, for keywords, letter case. These two statements are the same to the database:
select u.id,u.name,count(o.id) as orders from users u left join orders o on o.user_id=u.id where u.active=true group by u.id,u.name having count(o.id)>3 order by orders desc;
SELECT
u.id,
u.name,
COUNT(o.id) AS orders
FROM users AS u
LEFT JOIN orders AS o
ON o.user_id = u.id
WHERE u.active = TRUE
GROUP BY u.id, u.name
HAVING COUNT(o.id) > 3
ORDER BY orders DESC;
Only the second is something you can review. Formatting matters for the same reasons it does in other languages, and a few that are specific to SQL:
- Queries are read far more often than written. They live in migrations, reports, dashboards and application code for years.
- Structure shows intent. When each clause starts a new line, you can see at once which tables are joined, how, and what is filtered.
- Diffs stay meaningful. One column per line means adding a column changes one line, not the whole statement.
- Bugs hide in dense queries. A misplaced
ORin a longWHEREclause, or a join condition in the wrong place, is easy to miss when everything is on one line. - Performance work needs clarity. Tuning a slow query starts with understanding it.
Core formatting conventions
There is no single official SQL style, but widely used guides agree on most points. The SQL Style Guide by Simon Holywell is a popular reference, and it is a good starting point for a team standard.
Capitalise keywords. Write SELECT, FROM, WHERE in uppercase and leave table and column names in lowercase. This separates the language from your schema at a glance. (Some teams prefer all lowercase. Either is fine if consistent.)
Start each major clause on a new line. SELECT, FROM, JOIN, WHERE, GROUP BY, HAVING, ORDER BY and LIMIT each begin a line.
One column per line in the select list. It makes diffs and comments easy, and it is simple to reorder.
Indent consistently. Put the items under a clause one level deeper, with joins and their ON conditions indented under the table.
Always use AS for aliases. Explicit AS is clearer than a bare alias, and it prevents accidents when a comma is missing.
Use meaningful aliases. u, o and oi are fine when short and obvious. Avoid a, b, c.
Put operators and conditions where they are easy to scan. Align AND and OR at the start of lines in a long WHERE clause:
WHERE o.status = 'paid'
AND o.created_at >= DATE '2026-01-01'
AND (o.channel = 'web' OR o.channel = 'app')
Parenthesise mixed AND/OR. SQL evaluates AND before OR, which surprises people. Brackets state the intent.
Name things consistently. Use snake_case, singular or plural table names (pick one), and avoid reserved words as identifiers.
End with a semicolon. It marks statement boundaries and prevents errors when scripts are concatenated.
Writing readable queries beyond layout
Formatting is the visible part. These habits make queries easier to understand and safer.
Avoid SELECT *
SELECT * is convenient in exploration but fragile in code. It fetches columns you do not need, hides what the query depends on, and breaks when the table changes. List the columns you want.
Use explicit joins
Write JOIN ... ON rather than listing tables with commas and filtering in WHERE. Explicit joins separate the relationship from the filter, and they prevent accidental cross joins.
-- Hard to review: relationship hidden in WHERE
SELECT o.id, c.name
FROM orders o, customers c
WHERE o.customer_id = c.id AND o.status = 'paid';
-- Clearer: relationship in ON, filter in WHERE
SELECT o.id, c.name
FROM orders AS o
INNER JOIN customers AS c
ON c.id = o.customer_id
WHERE o.status = 'paid';
Break complex queries into CTEs
Common Table Expressions, written with WITH, name intermediate steps so a long query reads from top to bottom like a short program:
WITH recent_orders AS (
SELECT customer_id, SUM(total) AS spend
FROM orders
WHERE created_at >= CURRENT_DATE - INTERVAL '90 days'
GROUP BY customer_id
),
big_spenders AS (
SELECT customer_id
FROM recent_orders
WHERE spend > 1000
)
SELECT c.id, c.name
FROM customers AS c
INNER JOIN big_spenders AS b
ON b.customer_id = c.id;
Each block does one thing, and you can test it separately. CTEs are widely supported, including in PostgreSQL, SQL Server, MySQL 8 and SQLite.
Comment the why
A short comment explaining a business rule, a workaround, or a surprising filter is worth more than a comment restating the code.
-- Exclude test accounts created by the QA import (see ticket ENG-412)
WHERE u.email NOT LIKE '%@example.test'
Be careful with NULL
NULL is not equal to anything, including itself. WHERE col = NULL never matches. Use IS NULL and IS NOT NULL, and remember that NOT IN with a subquery returning NULL can return no rows.
Real-world use cases
Making sense of ORM or tool-generated SQL. Query logs, ORMs and BI tools produce one-line statements. Formatting is step one when debugging a slow or wrong query.
Code review. Reviewers can focus on logic when layout is consistent, and they can see exactly which lines changed.
Migrations and stored procedures. These files live for years. A consistent layout reduces mistakes during urgent changes.
Sharing queries. Whether you paste a query into a ticket, a chat or documentation, readable SQL gets faster, better answers.
Learning and teaching. Structured layout shows the logical order of a query: select, from, where, group, having, order.
Common mistakes
- One-line queries. Impossible to review and hard to diff.
- Implicit joins with filters in
WHERE. Hides relationships and invites accidental cross joins. - Mixed
ANDandORwithout brackets. Precedence bugs return wrong rows without any error. - Meaningless aliases.
t1,t2,xforce readers to scroll back and forth. - String-built queries with user input. Beyond style, this is a security hole. Use parameterised queries to avoid SQL injection. The OWASP SQL injection guidance explains the risk.
- Depending on column order.
INSERT INTO t VALUES (...)with no column list breaks when the schema changes. Name the columns. - Formatting data, not code. Reformatting string literals can change data. A good formatter leaves quoted strings alone.
Step-by-step: format SQL with DevUtilX
For a quick clean-up with no installation, use the DevUtilX SQL Formatter. It runs in your browser.
- Open the SQL formatter tool.
- Paste your query into the input editor.
- Choose your preferred indentation and keyword case.
- Run the formatter and read the structured output.
- Copy the result into your editor, ticket or documentation.
Because the formatting happens locally, the query text is not uploaded anywhere. That matters when a query contains table names, internal identifiers or sample data you would rather not share.
A formatter changes layout only. It does not validate your query against a database, so always run the result before relying on it.
Automating SQL formatting
On a team, formatting should happen automatically.
Use a SQL formatter or linter. Open-source tools such as sqlfluff can both lint and fix SQL for many dialects, and sql-formatter and similar libraries format queries in code. Pick one that supports your dialect, since PostgreSQL, MySQL and SQL Server differ in syntax.
Configure once, commit the config. Store the rules in the repository so everyone formats the same way.
Run in CI. Fail the build when a changed .sql file is not formatted, as you would for application code. Our guide to JavaScript code formatting describes the same pattern.
Keep it out of embedded strings, or format them deliberately. SQL inside application code is harder to format automatically. Keeping larger queries in separate .sql files makes them easier to lint, review and test.
Best practices
- Agree one style and enforce it with a tool. The best convention is the one everyone follows.
- Format on save, check in CI.
- Prefer explicit, named columns in
SELECTandINSERT. - Use CTEs to name steps in any query longer than a screen.
- Parenthesise boolean logic whenever
ANDandORare mixed. - Use parameterised queries, never string concatenation, for user input.
- Comment business rules, not syntax.
- Test queries on realistic data and review execution plans with
EXPLAINfor anything performance-sensitive. - Mind the dialect. Formatting rules are portable, but functions and syntax are not.
Comparison: style choices
| Choice | Option A | Option B | Advice |
|---|---|---|---|
| Keyword case | SELECT |
select |
Either; uppercase separates language from schema |
| Comma position | Trailing | Leading | Trailing is more common; leading eases commenting out the last line |
| Join style | Explicit JOIN |
Comma joins | Explicit, always |
| Subqueries | Nested | CTEs | CTEs for readability when queries grow |
| Aliases | Short | Descriptive | Short but meaningful |
None of these choices changes what the database does. They change how quickly a person can understand the query, so pick one set and keep it.
FAQ
Should SQL keywords be uppercase?
It is a common convention because it separates keywords from identifiers, and style guides recommend it. Lowercase is also acceptable. Consistency matters more than the choice.
Does formatting affect query performance?
No. The database parses the statement and ignores whitespace and keyword case. Performance depends on the query logic, indexes and data.
Are CTEs slower than subqueries?
It depends on the database and version. Some engines treat CTEs as optimisation fences, while others inline them. Check the execution plan for hot queries.
Leading or trailing commas?
Trailing commas are the more common style. Leading commas make it easier to comment out the last column without fixing punctuation. Choose one and apply it consistently.
Can a formatter break my query?
A correct formatter changes only whitespace and keyword case. Still, run the formatted query, especially if it contains dialect-specific syntax the tool may not fully support.
Do I need different formatting for different databases?
The layout rules are the same, but the syntax is not. Choose a formatter or linter that supports your dialect.
Conclusion
Readable SQL is a habit built from small, consistent decisions: capitalised keywords, one clause per line, one column per line, explicit joins, parenthesised logic and CTEs for long queries. Automate the layout so nobody has to think about it, and spend the saved effort on correctness and performance. For a fast clean-up of any query, try the SQL Formatter. To keep exploring, read our guides on HTML formatting and JSON formatting.
%20(1).jpg)
.jpg)
.jpg)