Skip to content
FormatKit

Free online SQL formatter and beautifier

SQL formatter that knows your dialect

Paste a wall-of-text query and it comes back as clauses you can read: SELECT list one column per line, each JOIN with its ON condition, every top-level AND indented under the WHERE it belongs to. It is free, needs no account, and reads standard SQL, MySQL, PostgreSQL and T-SQL — which matters, because those four disagree about whether a double-quoted run is a column or a string. Only two things ever change: the whitespace between tokens and the case of the 185 words recognised as keywords. Your literals, quoted identifiers and comments come out exactly as they went in.

  • 100% free
  • No signup
  • 4 dialects
  • Up to 5 MB
  • Nothing is executed
Formatted query

The clause-by-clause version appears here.

Paste a query, drop a .sql file, or press Ctrl+V anywhere on this page.

How to format a SQL query

Paste it, name the database, choose the house style.

  1. Drop the query in

    Paste it into the left panel, press the paste shortcut with nothing in particular selected, or open a .sql file straight from disk. A 200-line query pasted as one unbroken line is the normal case here, and so is a file holding forty statements separated by semicolons.

  2. Say which database wrote it

    The dialect selector is not decoration: it decides whether a double-quoted run is an identifier or a string, whether backticks and square brackets name a column, and whether # opens a comment. Pick MySQL for backticks, T-SQL for [Order Id] and @variables, PostgreSQL for $$ bodies and $1 placeholders, standard for everything else.

  3. Set the house style and take it

    Choose UPPERCASE, lowercase or as-typed keywords, 2 spaces, 4 spaces or a tab, and trailing or leading commas. Copy lifts the whole script; Download saves it as .sql. The counter underneath reports how many statements were found, how many keywords were recased and how deep the parentheses go.

Technical specifications

DialectsStandard, MySQL, PostgreSQL and T-SQL — the setting changes how quotes, comments and placeholders are read: backticks and # comments in MySQL, [brackets] and @variables in T-SQL, $tag$ bodies, E'…' strings and $1 placeholders in PostgreSQL, nested block comments in PostgreSQL only
Clause layout34 clause phrases start their own line (SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, UNION ALL, INSERT INTO, VALUES, SET, RETURNING, ON CONFLICT and the rest); 14 JOIN phrases indent one level with their ON condition beside them; a top-level AND or OR indents under its predicate; WHEN and ELSE indent inside CASE and END returns to the CASE column
Keyword casing185 recognised words — 148 keywords and type names plus 38 aggregate and scalar functions — switched to upper case, lower case, or left as typed. Identifiers, string contents and quoted names are never recased
Select listsOne item per line once a clause holds a comma at its own depth, with the comma trailing or leading; a column list in parentheses, an IN (…) set and a window OVER (…) clause stay on one line
Copied through unchangedString literals including doubled quotes, quoted and bracketed identifiers, $$-quoted function bodies, bind parameters (?, $1, :name), @variables and every comment
Not laid outMERGE branches, PIVOT and UNPIVOT, OVER (…) window definitions, table hints and vendor optimiser directives all stay inline; nothing is validated, so a query with a genuine syntax error is reformatted rather than rejected
Throughput200 statements in 71 KB take 17 ms; a 380-character single-line query becomes 18 lines. Limit 5 MB per script, with the whole file re-laid-out on every keystroke
What can changeWhitespace between tokens, and the case of recognised keywords. No token is added, removed or reordered, which is why the result parses wherever the original did — and why it never runs the query, in your browser or anywhere else

Frequently asked questions

Does formatting a query change its execution plan?

No — the planner never sees your whitespace, because the parser has already thrown it away by the time an execution plan is built. There is one second-order effect worth knowing: SQL Server and Oracle key the plan cache on the literal text of an ad-hoc statement, so a reformatted query is a cache miss and gets compiled once more, landing beside the old entry rather than replacing it. The plan that comes out is the same plan; you have simply paid for one more compile and one more cache slot. Parameterised statements and prepared handles are unaffected.

Why were some of my keywords left in lower case?

Because only the 185 words this formatter recognises get recased, and everything else is assumed to be your identifier. That is deliberate: a column genuinely called status, order or key must not be shouted at, and MySQL and PostgreSQL each add reserved words the other does not have. Anything inside quotes is untouchable in any dialect — a string literal keeps its case because changing it would change the data the query matches, and "Order Id" or [Order Id] keeps its case because in PostgreSQL and SQL Server a quoted identifier is case-sensitive.

Which dialect should I choose if I am not sure?

Start with standard, then switch if the output looks wrong around quotes. The tell for MySQL is a backtick, and MySQL is also the one dialect where "double quotes" hold a string rather than name a column — the others read that as an identifier, which is why picking the wrong one can turn a whole string into a column name in the output. The tell for T-SQL is [square brackets] or an @variable; the tell for PostgreSQL is $$, E'…' or a $1 placeholder. Only the reading changes, never the tokens.

Will a semicolon or a double dash inside a string break it?

No, because the reader tracks quotes before it looks for anything else. A literal such as 'a -- not a comment; really' is one token from the opening quote to the closing one, so neither the double dash nor the semicolon inside it can end a comment or split a statement. Doubled quotes are handled the way the standard defines them — 'it''s here' is a single value — and in MySQL and PostgreSQL a backslash escape is read as an escape rather than as the end of the literal.

Should the comma go before the column or after it?

After it, unless your team has already agreed otherwise, in which case the leading-comma switch is there. Leading commas exist for a practical reason rather than an aesthetic one: with the comma at the start of the line, commenting out the last column in a SELECT list does not leave a dangling comma behind, and adding a column produces a one-line diff instead of a two-line one. The cost is that the first item is the odd one out, and that most people find it harder to read at a glance.

Can it format a stored procedure or a PL/pgSQL function?

The statement around it, yes; the body inside dollar quotes, deliberately not. A $$ … $$ block is a string literal as far as PostgreSQL is concerned, and its contents are a different language with its own control flow — re-indenting it would mean writing a second formatter and guessing about the first. So CREATE FUNCTION, its argument list and its LANGUAGE clause are laid out, and the body between the dollar quotes arrives exactly as you wrote it. In T-SQL, a BEGIN … END block does get indented, because there the statements inside it are ordinary SQL.

What happens to my comments?

Every one is kept, and a comment that shared a line with code stays on that line. A double-dash comment ends the line it is on, so it is written out first and the code that followed it moves down; a /* block */ comment written mid-clause stays where it sits between the two tokens it separated. PostgreSQL is the only dialect here where block comments nest, and it is read that way, so an outer /* wrapped around code that already contains one does not end early and swallow half the query.

About formatting SQL

The reason a long query is hard to read is almost never the SQL — it is that the join graph is invisible. Twelve columns, four tables and a correlated subquery arrive as one paragraph, and the question you actually have is which table each column came from and what the rows were matched on. That is why the layout here puts every JOIN on a line of its own with its ON condition beside it, and why a top-level AND is indented one level under the WHERE it qualifies: those two rules alone turn the shape of the query into something you can take in without reading it word by word.

The dialect selector exists because SQL is a family of languages that agree on the verbs and disagree on the punctuation. The sharpest case is the double quote. In the ISO standard, in PostgreSQL and in SQL Server it delimits an identifier, so "Order Id" is a column with a space in its name — and, awkwardly, a case-sensitive one, which is why SELECT "id" fails against a table created with an upper-case name. In MySQL running with its default settings, the same quotes hold a string, and identifiers wear backticks instead; switch on ANSI_QUOTES and MySQL changes sides. SQL Server adds [square brackets], PostgreSQL adds dollar quoting so a function body can contain any quote character at all, and MySQL alone treats a leading # as a comment. A formatter that guesses at this does not merely mis-indent — it reads a string as a name and hands back something else.

The safety property worth stating plainly is that only whitespace and keyword case ever change. Tokens are never added, dropped or reordered, so the formatted query is the same query, and nothing here connects to a database: the text is laid out in your browser and never executed. When you need to see what actually changed between two versions of a statement, format both the same way first and then put them side by side in the code diff — identical formatting removes the noise and leaves only the predicate someone edited. The row of JSON that comes back from the query has its own formatter on the front page.

Where your query is formatted

Reading and printing are both done by JavaScript in this tab, so the query never leaves your machine and there is no database connection anywhere in this page. That matters more for SQL than for most formats, because a query pasted out of an incident channel routinely carries a customer ID, an email address or a table name nobody outside the company should see. Shut the tab and no copy of it remains.