Skip to content

Cookie settings

Optional analytics help us understand which pages and tools are useful. If you allow them, we use Google Analytics. Your files are never included, and every tool works the same if you decline. Cookie policy

How to Format SQL So It's Readable (Rules and Examples)

The formatting conventions that turn a wall of SQL into something you can review: keyword case, one clause per line, join and condition indentation, comma style and CTEs, with before-and-after examples.

By Jasper Caldwell9 min read

SQL ignores whitespace, so the database runs a 40-line query crammed onto one line exactly the same as a neatly laid-out one. People are the ones who struggle. Formatting is what lets a reviewer see that a join condition is missing, or that an OR is quietly overriding a filter. This guide covers the conventions most teams settle on, shows each one before and after, and explains the few places where formatting can actually change behavior.

Start with a messy query

Here is the kind of SQL you get from an ORM log, a BI tool or a colleague in a hurry:

select c.id,c.name,count(o.id) as order_count,sum(o.total) as revenue from customers c left join orders o on o.customer_id=c.id and o.status='paid' where c.created_at>='2026-01-01' and (c.country='DE' or c.country='FR') group by c.id,c.name having sum(o.total)>1000 order by revenue desc limit 20;

And here it is formatted with the conventions below:

SELECT
  c.id,
  c.name,
  COUNT(o.id) AS order_count,
  SUM(o.total) AS revenue
FROM customers AS c
LEFT JOIN orders AS o
  ON o.customer_id = c.id
  AND o.status = 'paid'
WHERE
  c.created_at >= '2026-01-01'
  AND (c.country = 'DE' OR c.country = 'FR')
GROUP BY
  c.id,
  c.name
HAVING SUM(o.total) > 1000
ORDER BY revenue DESC
LIMIT 20;

Nothing about the query changed, but you can now answer questions at a glance: which tables are involved, how they are joined, and what is filtered. You can also see that o.status = 'paid' sits in the ON clause, not the WHERE clause, which is deliberate in a LEFT JOIN because it keeps customers with no paid orders. In the one-line version, that distinction is easy to miss.

The core conventions

Keyword case

SQL keywords are case-insensitive, so select, SELECT and SeLeCt all work. The traditional convention is uppercase keywords and lowercase identifiers, which makes the structure of the query stand out from the names in it. The SQL Style Guide by Simon Holywell recommends uppercase reserved words, for example.

Lowercase keywords are also common, particularly in analytics teams and in editors with syntax highlighting, where color already separates keywords from names. Either is fine. Mixing them is not.

One caution: change the case of keywords, not identifiers. In PostgreSQL, unquoted names are folded to lowercase, while quoted names are case-sensitive, so "OrderTotal" and ordertotal are different columns. The PostgreSQL documentation notes that this folding differs from the SQL standard, which folds unquoted names to uppercase. A formatter that rewrites identifier case can break a query that uses quoted mixed-case names.

One clause per line

Start each major clause (SELECT, FROM, each JOIN, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT) on its own line at the left margin. The clauses then form a readable outline down the left edge.

When a clause has several items, put each on its own indented line. Short clauses with a single item can stay on one line, as HAVING and ORDER BY do above.

Indenting joins and conditions

Put each JOIN on its own line and its ON condition indented beneath it. When a join has more than one condition, give each AND its own line:

FROM orders AS o
INNER JOIN order_items AS oi
  ON oi.order_id = o.id
INNER JOIN products AS p
  ON p.id = oi.product_id
  AND p.is_active = TRUE

Use the same pattern in WHERE: one condition per line, each starting with its AND or OR. Starting the line with the operator means you can read the logic down the left side, and adding or removing a condition is a one-line change in a diff.

Always wrap mixed AND/OR logic in parentheses, even when you know the precedence. AND binds more tightly than OR, so WHERE a = 1 AND b = 2 OR c = 3 means (a = 1 AND b = 2) OR c = 3, which is a classic source of rows you did not expect. Formatting makes the grouping visible, but only if the parentheses are there.

Aliases

Write AS for column aliases and, where the dialect allows it, for table aliases. Prefer short but meaningful aliases (c for customers, oi for order_items) over a, b, c assigned in order. Oracle is the notable exception: it does not accept AS before a table alias, so omit it there.

Leading vs trailing commas

Trailing commas are the most common style:

SELECT
  id,
  email,
  created_at
FROM users;

Leading commas put the comma at the start of each line after the first:

SELECT
  id
  , email
  , created_at
FROM users;

The argument for leading commas is practical. When you comment out or delete the last column, a trailing-comma list leaves a dangling comma and a syntax error, while a leading-comma list does not (it has the same problem with the first column instead). Leading commas also make a missing comma easy to spot, since every line after the first should start with one. The argument against is that most people find trailing commas easier to read.

Both are defensible. The linter SQLFluff, for example, defaults to trailing commas and lets a team switch to leading ones (its rule LT04 is set with line_position in the SQLFluff layout rules). Pick one per codebase.

Common table expressions

CTEs (WITH clauses) are the single biggest readability improvement for long queries, because they let you name each step instead of nesting subqueries three levels deep. Format each CTE as its own block, with the name, AS (, an indented body and a closing parenthesis on its own line:

WITH paid_orders AS (
  SELECT
    customer_id,
    SUM(total) AS revenue
  FROM orders
  WHERE status = 'paid'
  GROUP BY customer_id
),

top_customers AS (
  SELECT customer_id
  FROM paid_orders
  WHERE revenue > 1000
)

SELECT
  c.id,
  c.name,
  p.revenue
FROM top_customers AS t
INNER JOIN customers AS c
  ON c.id = t.customer_id
INNER JOIN paid_orders AS p
  ON p.customer_id = t.customer_id
ORDER BY p.revenue DESC;

A blank line between CTEs makes each step easy to find. Name each CTE for what it contains (paid_orders), not how it was produced (cte1, subq).

Line length and long expressions

Break long expressions so a line stays under roughly 80–120 characters. For a CASE expression, put each WHEN on its own line:

CASE
  WHEN total >= 1000 THEN 'large'
  WHEN total >= 100 THEN 'medium'
  ELSE 'small'
END AS order_size

Dialect notes

Formatting rules are mostly portable, but the tokens being formatted are not. A formatter has to know which dialect it is reading, or it may misread a quoted identifier as a string or fail on syntax it does not recognize.

FeatureStandard / PostgreSQLMySQL / MariaDBSQL Server (T-SQL)BigQuery
Quoted identifier"name"`name`[name] or "name"`name`
Limit rowsFETCH FIRST n ROWS ONLY / LIMIT n (PostgreSQL)LIMIT nTOP (n) or OFFSET ... FETCHLIMIT n
Line comment---- (with a space) or #---- or #

Two details that matter for formatting:

  • In MySQL, -- only starts a comment when it is followed by a space or control character, per the MySQL comment syntax docs. A formatter that removes that space changes the meaning.
  • PostgreSQL allows nested block comments (/* a /* b */ c */), which many other dialects do not, and it uses dollar-quoted strings ($$ ... $$) for function bodies. Choose the right dialect so these are kept intact.

The SQL formatter on ToolsVerse lets you pick the dialect (PostgreSQL, MySQL, SQL Server, BigQuery, Snowflake, SQLite and others), along with indent width, keyword case and function case, so the output matches your house style. If you just want something readable without choosing settings, the SQL beautifier applies a preset such as "Readable" or "Tabular" in one click.

When to minify SQL instead

Formatting is for people. Sometimes you need the opposite: a query on a single line, for example as a value in a JSON or environment config, an argument on the command line, or a log entry that cannot span lines.

Minifying has one real trap: line comments. A -- comment runs to the end of the line, so if you collapse newlines without removing it, everything after the comment becomes part of it:

-- Before: two lines
SELECT id -- primary key
FROM users;

-- After naive minification: FROM users is now inside the comment
SELECT id -- primary key FROM users;

A proper minifier removes comments or keeps the line break after them. Be careful with block comments too: optimizer hints in MySQL and Oracle are written as comments (SELECT /*+ ... */), as described in the MySQL optimizer hints docs, so stripping every comment can silently drop a hint. The SQL minifier removes comments by default and has options to keep block comments or all comments.

Do not minify SQL that is stored in your repository. Version control diffs work line by line, so a one-line query turns every small change into a full-line rewrite that nobody can review.

Keeping a team consistent

Formatting only pays off when everyone does it the same way. A few habits help:

  • Write the rules down once. A short page covering keyword case, indent width, comma style and alias style settles most arguments. Adopting an existing guide is easier than writing one.
  • Automate it. Use a formatter or linter with a committed configuration file, and run it in your editor or in CI so nobody formats by hand. SQLFluff can both report and fix many layout issues.
  • Reformat in a separate commit. Mixing a full reformat with a logic change makes the logic change impossible to review. Format first, commit, then make the change.
  • Format before code review. Pasting a query through a formatter before you share it costs seconds and saves the reviewer from decoding it.

Format your query now

If you have a messy query in front of you, paste it into the SQL formatter, choose your dialect and keyword case, and copy the result. It runs in your browser, so the query is not uploaded, it is free and there is no account.

FAQ

Does formatting SQL affect performance?

No. The database parses the query into the same structure regardless of whitespace, line breaks or keyword case. The exceptions are comments that carry meaning, such as optimizer hints, which formatting should leave in place.

Should SQL keywords be uppercase or lowercase?

Either works, because keywords are case-insensitive. Uppercase is the traditional convention and is recommended by several style guides; lowercase is common in teams that rely on editor highlighting. Choose one and apply it everywhere.

Are leading commas better than trailing commas?

Neither is objectively better. Leading commas make it easier to comment out lines and spot a missing comma; trailing commas are more familiar to most readers. Consistency matters more than the choice.

Why did my formatter fail on valid SQL?

Usually because it was set to the wrong dialect. Backtick identifiers, square brackets, dollar-quoted strings and vendor-specific keywords are only recognized when the matching dialect is selected.

Is it safe to minify SQL?

Yes, if the minifier understands comments and strings. Check that -- comments are removed or followed by a line break, and keep block comments if your query relies on optimizer hints.

Tools for this