SQL

SQL formatter and beautifier

Consistent indentation, clauses on their own lines and syntax highlighting, for whichever engine you use. What sets it apart: it survives real queries — CTEs, window functions, nested subqueries and whole stored procedures with their control flow — which is where nearly every online formatter falls apart.

Zero uploads · all in your browser

Your query carries your database schema and sometimes real data. Here it never leaves the page.

Why this is not uploaded, and why it matters more here

A query is not neutral text. It carries the names of your tables and columns, which is your database schema. It carries the business logic written into the WHERE and the CASE. And very often it carries real data, because the query you paste is the one you were debugging: with the id of the customer who hit the problem, the user's email, the amount on the invoice that will not reconcile.

A formatter that processes on a server leaves all of that in somebody's log. No bad faith required: an access log rotating over ninety days is enough. And there is no technical reason for it: tidying the whitespace in a piece of text does not need a server. Here the engine is downloaded into your browser and works inside it. Load the page, disconnect from the internet and check.

How it works, in 3 steps

  1. Pick your engine and paste

    SQL Server, MySQL, PostgreSQL, Oracle or SQLite. It matters: the quoting, the comments and the procedural syntax all change. The result appears as you type.

  2. Adjust it to your house style

    Indent by 2, 4 or 8 spaces or tabs, keywords upper or lower case, and commas at the end of the line or the start. Your engine is remembered for next time.

  3. Copy, minify or parameterise

    Copy it, download it as .sql, flatten it to one line for a log, or swap its values for parameters and take the list away with you.

What people use it for

Where online formatters fall apart

Nearly all of them use the same library underneath, and that library handles queries very well: a common table expression with two WITH clauses, a couple of window functions and a four-branch CASE comes out immaculate. The problem is procedural code. Paste a stored procedure and this is what you get:

What most of them return
CREATE PROCEDURE
  dbo.UpdateStock @product_id int,
  @quantity int AS BEGIN
SET
NOCOUNT ON;

IF @stock IS NULL BEGIN RAISERROR ('not found', 16, 1);

ROLLBACK TRANSACTION;

END
UPDATE products
SET
  stock = stock - @quantity
What comes out here
CREATE PROCEDURE dbo.UpdateStock @product_id INT, @quantity INT AS
BEGIN
  SET NOCOUNT ON;
  IF @stock IS NULL
  BEGIN
    RAISERROR ('not found', 16, 1);
    ROLLBACK TRANSACTION;
    RETURN;
  END
  UPDATE products
  SET
    stock = stock - @quantity

The difference is not cosmetic: on the left there is not one level of indentation, so you cannot see what sits inside the IF or where the block ends — which is exactly what you opened it to find out.

Here the script is split into blocks first: what is a query goes to the library, and the procedural structure is indented by a purpose-built engine. That forces four things that sound trivial and are not:

  • END does not always close a block: a CASE ends the same way, and without keeping count, the first query with a CASE inside a procedure throws off every indent level below it.
  • BEGIN TRANSACTION does not open a block; a bare BEGIN does.
  • IF followed by a parenthesis is MySQL's IF() function, not a conditional.
  • SQL Server has no END IF: there, an END followed by an IF is two separate things — a block closing and another condition starting — while in Oracle and PL/pgSQL they are one piece. Confusing them swallows an entire condition.

What parameterising does, and why it matters

When you test something in the database editor you type the values by hand: where customer_id = 42 and status = 'pending'. When that moves into code, the values have to go as parameters, for two entirely unrelated reasons: so the engine reuses the execution plan instead of compiling a new one for every distinct value, and so you do not leave the door open to SQL injection.

The button does that step. The names come from the column each value is compared against, so the list reads itself: you get :customer_id and :status, not :p1 and :p2. You can pick between the four usual styles — :name, @name, $1 and ? — and take the value list as JSON or as plain text.

And some numbers are deliberately left alone, because they are not values: the 50 in varchar(50) is the size of the type; those in GROUP BY 1, 2 are column positions; and those in LIMIT or TOP stay because many drivers will not take a parameter there, and converting them would hand you a query that fails at run time.

Why you have to pick the engine

Because more changes than you would think, and not just the functions. How an identifier is quoted changes: square brackets in SQL Server, backticks in MySQL, double quotes in PostgreSQL and Oracle. What double quotes mean changes: in MySQL they delimit a string, in PostgreSQL a column name, so the same text is coloured and formatted differently. The hash comment exists only in MySQL. And the procedural syntax changes wholesale, as the END IF above shows.

Habits that make SQL readable

Formatting solves the whitespace. The rest is how you write, and it comes down to four habits you can pick up in an afternoon that save a lot of time for whoever reads it in six months — probably you.

The four that show the most

HabitWhy
One thing per line One column per line in the SELECT, one condition per line in the WHERE, one JOIN per line. Add or remove one and the diff points at that line and nothing else. It is what this tool does by default.
Short aliases that still mean something o for orders and c for customers can be followed; a, b and t1 make you scroll up every two lines. And always qualify columns with their alias: without that, adding a column to a table can make a query that had worked for years suddenly ambiguous.
WITH instead of nested subqueries Three levels of subquery read from the inside out, which is the opposite of how you think about them. Splitting it into named common table expressions puts them in the order they happen and gives every step a name.
Never SELECT * in production It fetches columns you do not use, stops the engine answering from the index alone, and the day somebody adds a column your code receives something it did not expect. Fine for a quick look; not for something you commit.

The same query written two ways

Hard work
select * from orders o, customers c
where o.customer_id=c.id and o.total>(select
avg(total) from orders where status='ok')
and c.country='GB' and o.date>'2026-01-01'
Readable
WITH average AS (
  SELECT AVG(total) AS amount
  FROM orders
  WHERE status = 'ok'
)
SELECT o.id, o.date, o.total, c.name
FROM orders o
  INNER JOIN customers c ON c.id = o.customer_id
  CROSS JOIN average a
WHERE o.total > a.amount
  AND c.country = 'GB'
  AND o.date > '2026-01-01'

The second version is not just prettier: the join is written with JOIN rather than commas in the FROM — so you can see which condition joins and which one filters — the subquery has a name, and it no longer drags in columns nobody uses.

If what you have is a CSV and you want the insert statements built for you, the site has a CSV to SQL converter that does exactly that.

Frequently asked questions

Is my query uploaded to a server?

No, and it matters here more than in almost any other tool. A query carries your table names, the business logic in the WHERE, and very often real data: the id of the customer who hit the problem, the user's email, the amount that will not reconcile.

A formatter that processes on a server leaves all of that in somebody's log, and there is no technical reason for it. Here the engine downloads into your browser. Once the page has loaded, disconnect from the internet and check.

Does it work with procedures, not just queries?

Yes, and it is why the tool exists. The library nearly every formatter uses handles queries well and procedural code badly: a procedure comes out on one line and unindented.

Here the script is split into blocks first: queries go to the library and the structure — BEGIN/END, IF/ELSE, WHILE, TRY/CATCH — is indented by a purpose-built engine that knows BEGIN TRANSACTION opens no block, that a CASE's END closes none, and that END IF exists in Oracle but not in SQL Server.

Why do I have to pick the engine?

Because the quoting changes — square brackets in SQL Server, backticks in MySQL, double quotes in PostgreSQL and Oracle — what a double quote means changes (a string in MySQL, an identifier in PostgreSQL), and the hash comment only exists in MySQL.

And the procedural syntax changes wholesale: SQL Server's IF reaches one statement, Oracle's opens a block until END IF. Picking wrong rarely breaks anything, but it produces odd details.

What does the parameterise button do?

It swaps literal values for named parameters and hands you the value list separately. It is the step of moving a query from a debugging session into code: there the values go as parameters so the engine reuses the execution plan and so there is no room for injection.

The names come from the column each value is compared against, so you get :customer_id rather than :p1. There are four styles: :name, @name, $1 and ?.

Which values are not turned into parameters?

The ones that are not values even though they look like it: the 50 in varchar(50) and the 10,2 in decimal(10,2) are the size of the type; those in GROUP BY 1, 2 and ORDER BY 2 are column positions and replacing them would break the query.

Those in LIMIT, TOP, OFFSET and FETCH are left because many drivers will not take a parameter there, and converting them would give you a query that fails at run time. Anything inside a comment is untouched too.

Does formatting change what the query does?

No. Only whitespace is touched and, if you ask, the case of the keywords. String contents are never touched: two spaces in a row inside a string stay, because there a space is data.

Comments are kept when formatting and removed when minifying. And minifying will not jam a word up against a quote even where the engine would accept it: it saves nothing that matters and makes the result unreadable.

Is there a size limit?

None imposed: the limit is your browser's memory and an ordinary script will not come close. Four hundred statements format in a couple of tenths of a second.

The only thing that changes above about two hundred thousand characters is that it stops reformatting on every keystroke and switches to doing it when you press a button, so the page does not sit thinking while you type. You are told when that happens.

What if my query has a syntax error?

As much as can be formatted, is. This does not validate and does not check that the query runs: it is a formatter, and its job is to tidy the text. A fragment it cannot read comes out with its spacing normalised rather than disappearing.

The one case flagged explicitly is an unclosed quote, because past that point there is no telling where the text ends and everything after it is misread. It is also the most common thing that happens when a pasted query got cut in half.

Related tools