SQL Formatter
Beautify SQL queries or minify them to one line
Working…
0.0
It hasn't frozen — the result will appear when the calculation finishes.
About SQL Formatter
This formatter is not a SQL parser. It splits the text into tokens, keywords, identifiers, numbers, strings, comments and punctuation, and then applies line-break rules: a new line for each clause such as SELECT, FROM, WHERE, JOIN and GROUP BY, known keywords in upper case, one line per item in a SELECT list and one per AND/OR in a WHERE clause. Single-quoted strings and comments are left untouched.
Because it does not parse, it cannot tell whether a query is valid, and it does not know every database's dialect. The sections below list which MySQL, PostgreSQL and SQL Server syntax it keeps intact, and which problems no amount of indentation will fix.
Dialect syntax it handles
In standard SQL, single quotes delimit strings and double quotes delimit identifiers, and a quote inside a string is doubled: 'It''s'. The tool understands that, MySQL's backtick identifiers such as `id`, and the dialect features below, which it keeps as single tokens.
- MySQL backslash-escaped strings such as 'It\'s' are kept exactly as written. If reading backslashes as escapes would leave a string unclosed, the tool falls back to the standard '' rule.
- MySQL's # line comment is recognized. Minify removes it like any other comment, so the next line is never swallowed. PostgreSQL's #>, #>> and #- stay operators, not comments.
- PostgreSQL's ->>, ->, @>, <@, || and the :: cast are kept whole; :: is written without spaces, as in created_at::date. Dollar-quoted bodies ($$ ... $$ or $tag$ ... $tag$) are treated as strings and left alone.
- Symbols that touch a string keep their original spacing, so array['a'] stays as array['a'].
- SQL Server's bracketed identifiers such as [order id] are kept as is, spaces included.
- Minify removes -- and /* */ comments but keeps optimizer hints (/*+ ... */) and MySQL's executable /*! ... */ comments, since those change how the query runs.
What formatting can't fix
The classic trap is operator precedence. AND binds more tightly than OR, so a = 1 OR b = 2 AND c = 3 means a = 1 OR (b = 2 AND c = 3). The formatter lines AND and OR up at the same depth, which invites you to read the three conditions as equals. If you meant (a = 1 OR b = 2) AND c = 3, you have to add the parentheses yourself.
The same goes for a LEFT JOIN filtered in the WHERE clause. In the first example below, the condition on o.created_at runs after the join; for users with no orders, o.created_at is NULL, the comparison is not true, and those rows disappear. The query quietly behaves like an INNER JOIN. To keep users without orders, move that condition into the ON clause. Clean formatting makes this easier to spot, but the tool will not point it out.
Likewise, col = NULL evaluates to unknown rather than true, so it never matches a row; you need IS NULL. A query with that mistake is valid SQL before and after formatting.
What the tool changes and what it leaves alone
- Only words on its keyword list are upper-cased: SELECT, FROM, LEFT JOIN, GROUP BY, CASE, AS and so on. Function and type names such as count, coalesce and date stay as you typed them.
- Indentation width is set in spaces. Subqueries are indented one extra level per parenthesis depth.
- Minify collapses whitespace to single spaces and removes comments (except /*+ */ hints and /*! */ comments). Runs of spaces inside strings, like 'x y', are preserved.
Worked examples
Tidying a one-line query
- Input
select u.id, u.name, count(o.id) as cnt from users u left join orders o on o.user_id = u.id where u.active = 1 and o.created_at >= '2026-01-01' group by u.id, u.name having count(o.id) > 3 order by cnt desc limit 10;- Output
SELECT u.id, u.name, count(o.id) AS cnt FROM users u LEFT JOIN orders o ON o.user_id = u.id WHERE u.active = 1 AND o.created_at >= '2026-01-01' GROUP BY u.id, u.name HAVING count(o.id) > 3 ORDER BY cnt DESC LIMIT 10;
Once it is laid out, you can see that the o.created_at condition in WHERE turns the LEFT JOIN into an inner join in practice.
AND and OR at the same depth
- Input
select * from t where a = 1 or b = 2 and c = 3- Output
SELECT * FROM t WHERE a = 1 OR b = 2 AND c = 3
The real meaning is a = 1 OR (b = 2 AND c = 3). Don't read logic off the line layout.
PostgreSQL operators kept intact
- Input
select id, data->>'name' as n, created_at::date from t where tags @> array['a']- Output
SELECT id, data ->>'name' AS n, created_at::date FROM t WHERE tags @> array['a']
->>, :: and @> each stay one token. ->> touches the string 'name', so the spacing between them is kept as written.
Minify with a hint and a # comment
- Input
SELECT /*+ INDEX(o idx_user) */ id FROM o # note WHERE a = 1- Output
SELECT /*+ INDEX(o idx_user) */ id FROM o WHERE a = 1
The optimizer hint survives and only the # note comment is removed. The WHERE clause on the next line stays intact.
How to use it
- Paste your SQL. Separate statements with semicolons.
- Choose the indent width, then Format or Minify.
- Compare the output with the original before using it.
Frequently asked questions
Does it find syntax errors?
No. It only rearranges tokens, so an invalid query is formatted just the same. Use your database's EXPLAIN or run the query to check it.
Why do function names stay lower case?
Only words on the built-in keyword list are upper-cased. Functions such as count or coalesce are left as typed.
Can I paste minified SQL into my code?
Yes, but ordinary comments are removed; only hints (/*+ */) and MySQL executable comments (/*! */) are kept. There is little or no performance benefit to minified SQL anyway.
Does it handle PostgreSQL operators?
Yes for ->>, ->, @>, <@, ||, :: and $$ dollar quoting, which are kept whole. There are no dialect settings, so compare the output if you use other vendor-specific syntax.
Does it support Oracle, SQLite or BigQuery?
There are no dialect settings. Anything close to standard SQL formats well; vendor-specific syntax not listed above may not.
Sources
- MySQL 8.0 Reference Manual: Comments
- MySQL 8.0 Reference Manual: String Literals
- PostgreSQL: JSON Functions and Operators
- PostgreSQL: Operator Precedence
- Microsoft Learn: Database identifiers (SQL Server)
Last reviewed: