SQL Formatter & Beautifier
How to Format SQL Online
Paste your SQL query into the input box on the left. Select your database dialect and formatting preferences from the options bar, then click Format SQL — or just start typing and the formatter runs automatically. The output panel shows clean, readable SQL with proper indentation and consistent keyword casing, ready to copy back into your editor or query tool.
Supported SQL Dialects
This formatter supports MySQL, PostgreSQL, SQLite, T-SQL (SQL Server), Standard SQL, and BigQuery. Selecting the correct dialect ensures dialect-specific keywords and functions are recognized and formatted correctly. For example, PostgreSQL supports :: casting syntax and RETURNING clauses, while T-SQL uses TOP instead of LIMIT.
SQL Formatting Best Practices
| Practice | Why It Matters |
|---|---|
| Uppercase keywords | Visually separates SQL keywords from identifiers and values, improving scan-ability |
| One clause per line | SELECT, FROM, WHERE, JOIN each on their own line makes diffs cleaner and logic easier to follow |
| Consistent indentation | Nested subqueries and conditions are much easier to trace with 2 or 4 space indentation |
| Alias with AS | Explicit AS keyword makes column and table aliases unambiguous and self-documenting |
| Avoid SELECT * | Explicit column lists prevent unexpected columns in results and improve query performance |
What Formatting Actually Changes
A formatter rewrites whitespace and keyword casing. It never changes what a query does — the parse tree is identical before and after — so it is safe to run on production SQL. Here is the transformation on a typical unformatted query.
select u.id,u.email,count(o.id) as orders
from users u left join orders o
on o.user_id=u.id where u.created_at>'2024-01-01'
and u.status='active' group by u.id,u.email
having count(o.id)>5 order by orders desc limit 20;
SELECT
u.id,
u.email,
count(o.id) AS orders
FROM
users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE
u.created_at > '2024-01-01'
AND u.status = 'active'
GROUP BY
u.id,
u.email
HAVING
count(o.id) > 5
ORDER BY
orders DESC
LIMIT
20;
Each clause keyword sits on its own line with its operands indented beneath it, so the shape of the query is visible before you read a single identifier. Note that count stays lowercase: the "uppercase keywords" option applies to SQL keywords, not function names, which keeps your own naming untouched.
The second version makes the join condition, the two filters, and the aggregate threshold individually visible. In review, that is the difference between spotting a missing AND and merging it.
Why Formatted SQL Matters in Review
Three concrete payoffs, beyond aesthetics.
Diffs become meaningful. When a query sits on one long line, changing a single condition marks the entire line as modified and the reviewer must compare two walls of text character by character. With one clause per line, the diff highlights exactly the clause that changed.
Missing join conditions become visible. A LEFT JOIN whose ON clause was forgotten produces a Cartesian product — a query that works fine against a small development dataset and takes down production. Indented and aligned, a join without its condition is obvious on sight.
Operator precedence stops hiding. WHERE a = 1 OR b = 2 AND c = 3 does not do what it appears to, because AND binds tighter than OR. Formatting puts each condition on its own line and makes the grouping — and the missing parentheses — apparent.
Dialect Differences That Matter
Selecting the right dialect matters because these constructs are not portable.
| Task | PostgreSQL | MySQL | T-SQL |
|---|---|---|---|
| Limit rows | LIMIT 10 | LIMIT 10 | TOP 10 |
| Quote an identifier | "column" | `column` | [column] |
| String concatenation | || | CONCAT() | + |
| Current time | NOW() | NOW() | GETDATE() |
| Cast a value | value::int | CAST(value AS SIGNED) | CAST(value AS INT) |
| Upsert | ON CONFLICT | ON DUPLICATE KEY | MERGE |
Choosing the wrong dialect usually shows up as a backtick-quoted identifier being treated as a string, or a :: cast being split across lines. If the formatted output looks structurally wrong, check the dialect selector before anything else.
Reading an Unfamiliar Query
Formatting is the first step; here is a reliable order for the rest.
- Start at
FROM, notSELECT. SQL executesFROMandJOINfirst, thenWHERE,GROUP BY,HAVING,SELECT, and finallyORDER BY. Reading in execution order rather than written order makes the logic follow naturally — and explains why a column alias defined inSELECTcannot be used inWHERE. - Identify the grain. Determine what one row of the result represents. If a join multiplies rows, every downstream aggregate is inflated — the most common cause of "our numbers are double."
- Read the CTEs top down. A well-written
WITHchain reads like a pipeline, each step named. If a CTE is used only once and is trivial, it is often clearer inlined. - Check
NULLhandling.NULL = NULLis not true, and aNOT INagainst a subquery containing a singleNULLreturns no rows at all. UseNOT EXISTSinstead.
Working Formatting Into Your Workflow
Ad-hoc formatting helps most when it is not ad-hoc. Adopt one style per repository and enforce it, so diffs never contain pure whitespace noise. Most teams settle on uppercase keywords, lowercase identifiers, two-space indentation, and leading commas or trailing commas chosen once and never revisited.
Format migrations before committing them, since a migration is read far more often than it is written — usually at an awkward moment, when someone is working out what changed. The same goes for queries embedded in application code and for the SQL that lives in analytics configs; if those configs are YAML, our YAML to JSON converter helps verify the nesting is right. For queries executed by scheduled reporting jobs, our cron expression generator confirms the schedule fires when you expect.
LEFT JOIN is doing a sequential scan. These are affiliate links — they cost you nothing and help keep these tools free.