codr.tools
Tools

SQL formatter / beautifier

Format and beautify SQL in your browser across 20 dialects, from PostgreSQL and MySQL to SQL Server, BigQuery and Snowflake, with your own indent width and keyword case, plus a minifier that puts a query back on one line.

Direction
Direction
In361 bytes
Out0 bytes
Swap
Ratio–
Status–

Loading the Standard SQL grammar…

The dialect decides which words are keywords and which quotes wrap a name. Standard SQL is the most forgiving choice when you are not sure.

Covers keywords, built-in function names and data types. Your table, column and alias names keep the case you wrote, and so does every string literal.

This lays SQL out. It does not check that the query is correct, does not connect to a database and never runs anything. Everything happens in this tab.

Format and beautify SQL in your browser across 20 dialects, from PostgreSQL and MySQL to SQL Server, BigQuery and Snowflake, with your own indent width and keyword case, plus a minifier that puts a query back on one line.

How to use this tool#

  1. Paste your SQL over the example. The example is there so you can see what the output looks like before you commit anything. Formatting runs as you type, so there is no button to press.
  2. Pick the dialect. It decides which words count as keywords and which quotes wrap a name, so a MySQL query with backticks wants MySQL and a T-SQL query with[brackets] wants SQL Server. Standard SQL is the most forgiving choice when you are not sure what you are looking at.
  3. Set the indent width and the keyword case. Two, four or eight spaces, or a tab. Keywords can come out uppercase, lowercase, or exactly as you wrote them.
  4. Switch to Minify to go the other way: the whole statement back on one line, with the comments removed, for pasting into a string literal or a log line.
  5. Read the messages under the output. An unclosed quote or comment is reported with the line, the column and the offending line with a caret under it.

Everything runs in your browser. Nothing you paste is sent anywhere, which matters more here than on most tools: queries carry table names, column names and, often enough, a literal value out of production.

What formatting actually changes#

A SQL formatter rewrites whitespace and, if you ask, the case of keywords. It does not reorder clauses, rename anything, add or remove parentheses, or change what the query does. The point is to make the shape of a statement visible: one column per line, each clause starting in the same column, and nesting shown by indentation rather than by counting brackets.

before
select o.id,c.name,o.total from orders o join customers c on c.id=o.customer_id where o.total>100 and o.status in ('paid','shipped') order by o.total desc
after
SELECT
  o.id,
  c.name,
  o.total
FROM
  orders o
  JOIN customers c ON c.id = o.customer_id
WHERE
  o.total > 100
  AND o.status IN ('paid', 'shipped')
ORDER BY
  o.total DESC

The second version is not shorter. It is easier to review, easier to diff, and easier to argue about, because a reviewer can see at a glance that the join has one condition and the filter has two. That is the whole return on formatting.

Nesting is where it earns its keep fastest:

sql
SELECT
  a
FROM
  t
WHERE
  id IN (
    SELECT
      id
    FROM
      u
    WHERE
      x = 1
  )

Keyword case, and why teams fight about it#

SQL keywords are case-insensitive in every dialect here, so SELECT andselect are the same word to the database. The case is purely for the reader, which is exactly why it becomes a matter of taste.

Uppercase keywords are the older convention and still the most common in documentation and in hand-written scripts: the keywords are the scaffolding and the lowercase parts are your names. All-lowercase has become the norm in codebases where SQL sits inside application code, on the argument that shouting is noise once syntax highlighting exists. Both are defensible. Pick one and let the formatter enforce it, which is the actual win.

sql
-- Input
select Id, cast(Total as decimal(10,2)) from Orders where Status is not null

-- Keyword case: UPPERCASE
SELECT
  Id,
  CAST(Total AS DECIMAL(10, 2))
FROM
  Orders
WHERE
  Status IS NOT NULL

-- Keyword case: lowercase
select
  Id,
  cast(Total as decimal(10, 2))
from
  Orders
where
  Status is not null

The Keyword case option here also covers built-in function names and data types, so cast and decimal move with select. Your table, column and alias names are never touched, and neither is anything inside a string literal: in the example above Id, Total, Orders andStatus keep the capitals you gave them.

Dialects, and why the choice matters#

The formatter behind this page supports twenty dialects: standard SQL, PostgreSQL, MySQL, MariaDB, SQLite, SQL Server (Transact-SQL), Oracle (PL/SQL), BigQuery, Snowflake, Amazon Redshift, ClickHouse, DuckDB, SingleStoreDB, Apache Spark SQL, Apache Hive, Trino and Presto, IBM Db2, Db2 for i, Couchbase N1QL and TiDB.

Picking the right one changes two things: which words are treated as clause keywords worth a line of their own, and which characters start a quoted name. Get the second one wrong and the formatter reads a name as a string, or a string as code.

text
PostgreSQL, Oracle, Db2, Trino, standard SQL
  "odd name"        a name
  'odd name'        a string

MySQL, MariaDB, TiDB, SingleStoreDB
  `odd name`        a name
  "odd name"        a string, unless ANSI_QUOTES is on

SQL Server (T-SQL)
  [odd name]        a name
  "odd name"        a name, while QUOTED_IDENTIFIER is on

SQLite
  "odd name"        a name, or a string if no such name exists
  `odd name`        a name, for MySQL compatibility
  [odd name]        a name, for SQL Server compatibility

BigQuery, ClickHouse, Spark, Hive
  `odd name`        a name

SQLite is the permissive one: it accepts MySQL's backticks and SQL Server's brackets on purpose, for compatibility, and it has one more trick. A double-quoted word that does not match any column or table is accepted as a string literal rather than rejected, which its own documentation calls a misfeature kept for backwards compatibility. RunningSELECT "no such column" on SQLite 3.50.4 returns the textno such column, not an error.

Comments are not the same everywhere either

text
Everywhere            -- to end of line       /* block */
MySQL family          # to end of line        -- needs a space after it
BigQuery, ClickHouse  # to end of line
Snowflake             // to end of line
PostgreSQL            /* may be /* nested */ */

The MySQL rule is the one that catches people. MySQL's manual requires the double dash to be followed by at least one whitespace or control character before it counts as a comment, which the other dialects do not. So a line that is a comment in PostgreSQL is arithmetic in MySQL:

sql
-- PostgreSQL, SQLite, SQL Server, Oracle: a comment.
SELECT 1--2;

-- MySQL: not a comment. The second dash needs whitespace after it,
-- so this is 1 minus minus 2, which is 3.
SELECT 1--2;

-- Unambiguous everywhere:
SELECT 1 - -2;

This tool applies each dialect's own rule, which is why switching the dialect can change where the comments are. If you are writing SQL that has to survive being pasted between engines, put a space after the dashes and never rely on #.

Dollar quoting

PostgreSQL, Redshift and DuckDB let you wrap a string in $$ or a tagged$body$ instead of single quotes, and Snowflake takes the plain $$form. It is how a function body avoids escaping every quote inside it. Everything between the two markers is text, including semicolons and line breaks, and this tool leaves all of it exactly as you wrote it.

sql
CREATE FUNCTION bump(n int) RETURNS int AS $$
  SELECT n + 1;   -- quotes, semicolons and newlines are all just text here
$$ LANGUAGE sql;

Minifying SQL#

Minify is the opposite trip: whitespace runs collapse to a single space, the space beside a comma, a semicolon or a bracket disappears, and the statement comes back as one line. It is what you want when a query has to go into a string literal, a JSON field, a one-line log entry or apsql -c argument.

sql
-- Before
SELECT
  o.id,
  c.name
FROM
  orders o
  JOIN customers c ON c.id = o.customer_id
WHERE
  o.total > 100;

-- After
SELECT o.id,c.name FROM orders o JOIN customers c ON c.id = o.customer_id WHERE o.total > 100;

Two things are deliberately left alone. Comments are removed, because on a single line a -- comment would swallow everything after it and there is nowhere safe to keep them. And spacing around operators stays, even though it looks like free bytes:

sql
-- The one join that is never made:
SELECT a - -1 FROM t      stays   SELECT a - -1 FROM t
                          never   SELECT a--1 FROM t

-- Because "--1 FROM t" would be a comment, and the query would
-- turn into "SELECT a", silently, with no error anywhere.

A minifier that tightened that gap would turn a subtraction into a comment and quietly change the result. Saving two characters is not worth a query that returns the wrong number, so the space stays. The inside of a string literal is never touched at all, spaces, line breaks and all, so a value keeps its exact contents.

Minified SQL is for machines. If you are storing a query in source control, store the formatted version: the diff of a one-line query is the whole line, every time.

Where the formatter stops#

The library behind this page states its own limits plainly, and they are worth knowing before you paste a migration into it.

  • Stored procedures are not supported. The statements inside aBEGIN … END body are still laid out, but the procedure wrapper around them is not, so the result looks half finished. It is valid SQL, just not pretty.
  • The statement separator is always ;. A MySQL script that usesDELIMITER // to define a procedure will have the // read as two division signs.
  • It is not a validator. A parse error here means the grammar could not follow the statement, which is often a syntax mistake but can equally be a vendor extension the grammar does not cover. If a statement you know is valid is rejected, try Standard SQL, which has the loosest grammar of the twenty.
  • Keyword tables differ slightly between dialects. A word that is uppercased under Standard SQL may be left alone under MySQL, because each dialect carries its own list.
sql
-- Input
CREATE PROCEDURE add_user(IN n VARCHAR(50))
BEGIN
  INSERT INTO users (name) VALUES (n);
END

-- Output: the body is laid out, the procedure around it is not
CREATE PROCEDURE add_user (IN n VARCHAR(50)) BEGIN
INSERT INTO
  users (name)
VALUES
  (n);

END
sql
-- Input
DELIMITER //
CREATE PROCEDURE p() BEGIN SELECT 1; END //
DELIMITER ;

-- Output: // is read as two division signs
DELIMITER / /
CREATE PROCEDURE p () BEGIN
SELECT
  1;

END / / DELIMITER;

For procedure-heavy scripts, format the statements and leave the wrapper alone, or reach for a tool built on a full dialect parser, such as your database's own IDE.

Formatting SQL on the command line#

This page is built on sql-formatter, which ships the same formatter as a command, so a script can produce byte-for-byte what you see here.

shell
# The same formatter this page uses, as a command.
npx sql-formatter -l postgresql query.sql

# In place, with a config file beside it.
npx sql-formatter -l mysql --fix schema.sql

# From a pipe, with the options inline.
cat query.sql | npx sql-formatter -l sqlite -c '{"keywordCase":"upper","tabWidth":4}'

# It reads .sql-formatter.json from the working directory when -c is left out.

Options go in a .sql-formatter.json file, which the command picks up from the working directory:

.sql-formatter.json
{
  "language": "postgresql",
  "keywordCase": "upper",
  "dataTypeCase": "upper",
  "functionCase": "upper",
  "tabWidth": 2,
  "useTabs": false,
  "linesBetweenQueries": 1
}

It is not the only option, and for some jobs it is not the best one.SQLFluff is a linter first and a formatter second, which is what you want in CI, where the interesting question is whether a file conforms rather than what it should look like.

shell
# SQLFluff 4.3.0: a linter first, with a formatter attached.
sqlfluff format query.sql --dialect postgres     # rewrites the file
sqlfluff lint query.sql --dialect postgres       # reports without rewriting

# Its settings live in a .sqlfluff file, INI style:
#   [sqlfluff:indentation]
#   indent_unit = space
#   tab_space_size = 4
#
#   [sqlfluff:rules:capitalisation.keywords]
#   capitalisation_policy = upper

pgFormatter targets PostgreSQL and PL/pgSQL specifically, and handles the procedural parts this tool skips.

shell
# pgFormatter 5.11: PostgreSQL and PL/pgSQL, written in Perl.
pg_format -s 4 -u 2 query.sql            # 4-space indent, uppercase keywords, to stdout
pg_format -i -s 2 query.sql              # rewrite the file in place
pg_format -o pretty.sql query.sql        # write to another file

# -u takes 0 unchanged, 1 lowercase, 2 uppercase, 3 capitalized.

There is a Prettier plugin as well, prettier-plugin-sql, which wraps the same formatter so a .sql file joins the rest of your prettier --write run. Its own README is honest that the printer is a wrapper and may not match Prettier's house style. And every serious database IDE has a formatter built in, usually under a keyboard shortcut and usually with more knobs than any of these.

Questions people ask#

Is my SQL uploaded anywhere?

No. The formatting happens in your browser, in JavaScript, after the page has loaded. There is nowhere to send it: this site is static files with no backend of any kind. Disconnect from the network and the tool keeps working, which is the quickest way to prove it. That matters for SQL in particular, since a query often carries production table names and a real value or two.

Does this validate my SQL?

Not in the way you probably mean. It checks two things: that every quote and comment you opened is closed, and that the statement fits the chosen dialect's grammar well enough to be laid out. It does not know whether your tables exist, whether the columns are spelled right, whether the join makes sense or whether the query will run. Only the database can tell you that. Run it against a copy with EXPLAIN in front of it.

Why does my query get rejected when it works fine in my database?

Usually a dialect mismatch, sometimes a feature the grammar does not carry. Try the dialect your query is actually written for, and then Standard SQL, which accepts the most. Stored procedure bodies, vendor-specific hints and non-; delimiters are the common causes.

Will it change what my query does?

It should not. Formatting only moves whitespace and, where you ask for it, changes the case of keywords, built-in function names and data types, all of which are case-insensitive. Names and string literals are left exactly as written. The one thing to be careful with is the dialect: pick the wrong one and a name quoted in a style that dialect does not know can be read as something else. Always check the output of a formatter before committing it, the same as with any other automated rewrite.

Which dialect should I pick if I do not know?

Standard SQL. It has the loosest grammar and the largest common keyword set, so it formats the widest range of input. Move to a specific dialect when you are using something that only that dialect has: backticks or # comments mean MySQL, square brackets and@variables mean SQL Server, $$ bodies mean PostgreSQL.

Can it format several statements at once?

Yes. Paste a whole script and every statement ending in a semicolon is formatted, with a blank line between them. The one thing to watch is a script that changes the delimiter, which is not supported.

Does minifying make my query faster?

No. Whitespace is stripped by the parser before the planner ever sees the statement, so a minified query and a formatted one produce exactly the same plan. Minify for transport, not for speed: a one-line query fits in a log entry, a JSON field or a string literal in your code.

What happens to my comments?

Formatting keeps them, on the line they belong to. Minifying removes them, because a-- comment on a single line would comment out the rest of the query. If you need the comments and the single line, keep the formatted version as the source and generate the minified one when you need it.

How large a query can it handle?

Larger than you are likely to paste. The limit is your machine rather than an upload size, since nothing leaves the tab. The output box shows the first 200,000 characters to keep scrolling smooth, while copy gives you the whole result.

Can I use tabs instead of spaces?

Yes. Choose Tab under indentation and each level of nesting is one tab character, which lets every reader set their own width.

  • Format, minify and validate JSON in your browser, with syntax errors explained by line and column, repeated keys listed, and number literals kept exactly as written.

  • Convert YAML to JSON and JSON back to YAML in your browser, following YAML 1.2, with anchors and merge keys resolved, multi-document streams, and syntax errors reported by line and column.

  • Pretty-print, minify and check XML in your browser, with well-formedness errors reported by line and column, and comments, CDATA sections, processing instructions and mixed content all kept intact.

  • Convert identifiers and text to camelCase, PascalCase, snake_case, SCREAMING_SNAKE_CASE, kebab-case, Train-Case, dot.case, path/case and four more at once, one line at a time, with acronyms and digits split the way style guides expect.