SQL Formatter
Format SQL for PostgreSQL, MySQL, SQL Server and five other dialects — with a check that refuses the output if formatting changed any token.
the formatted query appears here
What this page does with your query
Paste a query of up to 100,000 characters and it is formatted as you type; anything longer waits for Format or ⌘⏎ Ctrl+Enter , so a migration file is not re-formatted on every keystroke. The layout: one clause per line, the select list and conditions indented under their keyword, statements separated by a blank line. Pick the dialect, 2 or 4 spaces or tabs, and whether keywords, data types and function names are left as typed, upper-cased or lower-cased. The formatting is done by sql-formatter 15.9.0, an open-source library, running in a worker thread in this tab — the page stays responsive on a long migration file, and nothing is uploaded.
What this page adds is a check on the library's output. After every format it reads the input and the output with its own lexer, compares the two token sequences, and refuses the output when a token changed. Whitespace is allowed to change, and letter case when you asked for it. Anything else — a string, an identifier, a comment, an operator — has to come back exactly as it went in, or there is no output to copy.
Why a formatter needs checking
A formatter should only move whitespace around. To do that it has to decide where every token starts and ends, and when it decides differently from your database, the query it hands back is a different query. Measured with sql-formatter 15.9.0, which otherwise round-trips ordinary SQL cleanly — formatting its own output a second time changes nothing — three inputs come back with a token split in two, and none of them raises an error:
| Input | Formatter output | What changed |
|---|---|---|
select /* abc from t |
select / * abc from t |
An unterminated comment becomes a division and a multiplication. Everything after /* was comment; now it is code. |
DELIMITER // (MySQL) |
DELIMITER / / |
The delimiter a stored-procedure script sets for the mysql client is split into two tokens. |
a || 'x' (SQL Server) |
a | | 'x' |
|| is string concatenation in SQL Server 2025 and Azure SQL; two bitwise ORs are not. |
This page refuses all three and names the line and column of the first token that changed. The check is a comparison of token sequences, not a proof that two queries are equivalent — it is only as good as its lexer's agreement with your database's, and the FAQ below says where that ends.
Errors that say where
When a query does not parse, the library's error is a parser trace: a line and
column, then every grammar rule that could have continued, dozens of lines of them
for a single missing parenthesis. This page keeps the line and column and replaces
the rest with one sentence — an unclosed (, a string that is never
closed, a ) with no match, :: in a dialect that has no
casts, a psql meta-command such as \c, or dbt template syntax. Then, for
a query of up to 100,000 characters, it tries the other dialects and offers any that
format it cleanly, as a one-click switch, because the commonest parse error is the
right query under the wrong dialect. Above that size it does not retry, and says so;
offline, it tries only the dialects already downloaded on this device, and says how
many it skipped.
The same thing from a terminal
# The same library this page runs, as a CLI. -l picks the dialect; -c takes JSON options.
echo "select a::int, b from t where x = :p" | npx sql-formatter -l postgresql \
-c '{"keywordCase":"upper","paramTypes":{"numbered":["$"],"named":[":"]}}'
# SELECT
# a::int,
# b
# FROM
# t
# WHERE
# x = :p
# The case the token check exists for, reproduced with the CLI (sql-formatter 15.9.0): echo "select /* abc from t" | npx sql-formatter -l postgresql # select # / * abc # from # t # The input was a keyword and an unterminated comment. The output is live code.
# In CI: format in place, then fail if that changed anything (tested with sqlfluff 4.1.0). # "sqlfluff format" exits 0 even when it rewrites a file, so on its own it never fails a build; # "git diff --exit-code" exits 1 when the checkout now differs from what was committed. sqlfluff format --dialect postgres queries/ git diff --exit-code -- queries/
import sqlparse # tested with 0.5.5
print(sqlparse.format("select a,b from t where x=1", reindent=True, keyword_case="upper"))
# SELECT a,
# b
# FROM t
# WHERE x=1
There is no formatting standard — the references are the lexers
No specification says how SQL should be laid out; every formatter's style is a
choice, and this page makes the library's defaults. What is specified is where
tokens begin and end, and that is what the check leans on. The
PostgreSQL
lexical structure chapter defines dollar quoting, E'' escape strings
and nested block comments. MySQL's
stored
programs page is where DELIMITER is explained, and it is a client
command, not SQL. Microsoft's
||
reference lists SQL Server 2025, Azure SQL Database, Azure SQL Managed Instance
and Fabric.
Two neighbours on the same workflow: diff, for comparing two formatted queries — an ORM's SQL before and after a change — and JSON unescape, for SQL that arrives inside a JSON log line.
FAQ
Why did this page refuse to format my query?
The formatter's output failed the token check. Whitespace may change, and case when
you asked for it; any other token that differs means there is no output to copy. The
usual cause is an unterminated /*, which the formatter splits into
/ *. The page names the line and column and leaves your input alone.
Can formatting SQL change what the query does?
It can: all three measured cases above are one token split into two. This page checks the formatted output for token changes and refuses it when it finds one. That is a comparison of token sequences made with this page's own lexer — not a proof of equivalence. A construct that this lexer and your database read differently could still pass.
Why is the default dialect PostgreSQL?
Because the PostgreSQL mode handles the syntax this page tests — ::
casts, $$ quoting, E'' strings, ->> and
@>, nested comments — while the standard-SQL mode rejects
a::int, $$…$$ and :name. It is not a claim
about which database most people use. The picker is above the input, and a failed
parse of a query up to 100,000 characters offers the dialects that do format it.
How do I put a query on one line?
Not safely with a formatter, and there is no minify mode here. A --
comment runs to the end of its line, so joining lines turns the rest of the query
into comment text. Rewrite each -- comment as /* */ first,
or, with no -- comments, join the lines by hand.
Why was the indentation inside my block comment changed?
The formatter re-indents the continuation lines of a /* */ comment to
match the surrounding code — six spaces came back as two. The check treats
whitespace inside a comment as whitespace, so it passes. Check any comment where the
spacing matters.
Does it format dbt models or other Jinja-templated SQL?
No. {{ ref('orders') }} and {% if %} are template
syntax; a model is not SQL until dbt renders it. The page stops at the first
{{ and says it found template syntax. Format the compiled SQL from
dbt's target/ directory instead.