Code Formatting

SQL Formatter

Format, compress and pretty-print SQL queries.

Input SQL
Formatted result
Formatted result will appear here

What is SQL Formatter?

SQL formatter beautifies SQL queries with proper indentation and keyword formatting, making complex queries easier to read and understand.

Features

  • Format: Beautify with indentation
  • Keyword Case: Uppercase/lowercase keywords
  • Multi-dialect: MySQL, PostgreSQL, SQL Server support

Use Cases

  • Query debugging
  • Code review
  • Documentation

A text-level SQL re-printer: it uppercases or lowercases 55 hard-coded keywords and drops a line break wherever one of them is glued to the next character, with 2-space, 4-space or 1-space indentation plus a whitespace-collapsing minify view. Useful for tidying one-line queries pasted out of logs, tickets or ORMs; it never parses or validates.

Two text passes, no grammar

The formatter never builds a syntax tree. Pass one walks a fixed list of 55 keywords — SELECT, FROM, WHERE, JOIN, the aggregates, and multi-word forms such as GROUP BY and UNION ALL — and rewrites each match with a case-insensitive, word-boundary regular expression, so casing changes wherever the word occurs, quoted string literals included. Pass two is a character loop that tracks single, double and backtick quoting, emits a line break when a keyword sits immediately before a non-space character, and raises the indent level after FROM, WHERE, JOIN, GROUP BY, ORDER BY and HAVING. With no grammar behind it nothing can be rejected: a typo such as SELEC or an unbalanced parenthesis is reprinted as-is and the error row stays empty.

Why breaks land after the keyword

The break test compares the characters immediately before the current index against the keyword, so the newline is written after the keyword rather than before it: COUNT(o.id) comes back as COUNT, a newline, then (o.id). That test also has no left word boundary — only a whitespace check — so the two-letter keyword OR matches the opening of order_count and yields or, a break, der_count; note is split the same way by NOT. The indent counter only ever grows: the six clause keywords each add a level and nothing removes one until a semicolon resets it to zero, so a nested subquery stays pushed right of its parent. Input whose keywords are all followed by a space survives on one line with casing as the only change.

Minify collapses whitespace, not syntax

The minify panel runs two substitutions and a trim: every whitespace run becomes one space, then whitespace around (, ), , and ; is deleted. Comments are never stripped, and because the newline that ended a -- comment is collapsed too, whatever followed on the next line joins the comment and stops being SQL: a filtered select with a trailing note minifies into one long comment. The punctuation rule is equally blind to quoting, so ('a, b', 'c, d') becomes ('a,b','c,d') — still valid syntax, but no longer the same literals. Dollar-quoted bodies keep both $$ markers while losing their internal line structure, and count(*) loses the space that separated it from the following keyword.

Worked example

Formatting a one-line subquery at the default 2 spaces

Input
select * from(select id from t where x = 1)
Output
SELECT * FROM
  (SELECT id FROM t WHERE x = 1)

FROM is directly followed by (, so that is the single position where the break fires, and FROM is also one of the six keywords that raises the indent level — hence exactly two leading spaces on the second line. Every other keyword here is followed by a space, which is why the rest of the query stays on one line. At 4 spaces the second line starts with four; the "Tab" option stores indent 1, so it starts with a single space.

Limits and pitfalls

  • Indent is offered as 2 spaces, 4 spaces or "Tab", but the Tab entry sets the width to 1 and the component repeats a space character, so it produces one space per level, never a tab.
  • Nothing is validated: misspelled keywords and unbalanced parentheses are reprinted silently and the error line under the output stays empty.
  • No comment, dollar-quote or bracket-identifier awareness — a ; inside a PostgreSQL $$ ... $$ body still counts as a statement end and resets indentation.
  • Only the format and minify passes are local; the AI Optimize button posts the whole query to the /ai/generate endpoint before rewriting the input box.
  • There is no input length cap and the whole string is re-processed on every keystroke, so multi-megabyte dumps make the panel sluggish.

Related tools

When a text-level re-printer is the right call, and when it is the wrong one

What you needSQL formatter on this pageWhere to go instead
The statement uses syntax the keyword table has never seen: CTEs, window functions, dollar-quoted function bodiesEverything is substring matching against 55 words. WITH, OVER, PARTITION, RETURNING and the $$ markers are not in that table, so they keep the case you typed while the words beside them are flipped. Tolerable on a plain SELECT, misleading on a PL/pgSQL function body.A formatter with per-dialect tokenizers: sql-formatter on Node — npx sql-formatter query.sql reprints to stdout and takes the dialect as a command-line option — or pg_format from pgFormatter. Reach for one when a function body or a migration must come back intact.
You are asking "will this run", not "what does it look like"The output pane fills for any text at all, so a clean result proves nothing about the statement: the eye is the only parser in the loop, and it has to spot the missing FROM itself.A parser-backed check — sqlfluff lint q.sql, or psql -f q.sql against a throwaway database — returns a non-zero exit on a broken statement. Keep it in the pre-commit hook; keep this page for reading.
You want one column per line from a single-line queryThis is the one layout job it does well, and it depends on punctuation rather than grammar: a comma with no space after it gets pushed to the start of a new line, so a SELECT list written as a.id,a.email,a.total comes out stacked, commas leading.If your style guide wants trailing commas, or commas aligned under the opening keyword, a grammar-based formatter is the only way there. Use this page when leading commas are acceptable; switch when they are not.
The keyword-case rule has to be reproducible by a teammateCase is a two-position switch — all 55 table words upper, or all of them lower — and words outside the table never move, so desc, nulls, over stay exactly as typed. No config file is read or written, so nobody can replay your settings in a hook.A formatter driven by a checked-in config: prettier with prettier-plugin-sql, or sql-formatter's own config file. The same settings then run in the editor, in pre-commit and in CI. Choose it as soon as a second person formats the same queries.
Two hundred migration files rather than one pasted queryOne textarea, one query, no file input and no glob; the whole string is re-scanned on every keystroke. The only outbound path is the AI button in the toolbar, so leaving it untouched keeps the SQL on the machine.A shell loop: for f in migrations/*.sql; do npx sql-formatter "$f" > "$f.new"; done. Same text-out, no browser, reviewable as a diff. Spot-check a single query here, run the loop over the tree.

One concrete scenario

Turning a generator-style one-liner into eleven lines, and what that layout costs

A report query arrives from a code generator with no spaces after commas: 178 characters, one line, nine commas, five select columns. Nine of those commas each start a line of their own, the comma leading and the column following it unindented, and the trailing semicolon adds a last line holding nothing else. Ten inserted newlines are the entire difference between the 178 in and the 188 out, spread over 11 lines. Every clause keyword in it was followed by a space, so SELECT, FROM, WHERE and GROUP BY never broke, and the 1-, 2- and 4-space settings return byte-identical output because no clause keyword triggered a break. If a leading-comma column list is what you want, this output is usable; if your reviewers expect trailing commas, retype them or hand the file to a grammar-based formatter.

Frequently Asked Questions

Does formatting execute or modify my SQL?
No. The tool parses text locally and re-prints it — no database connection, no execution. Pasting production SQL with table names or internal hosts is safe; still avoid pasting real data out of habit.
Which SQL dialects are supported?
Common syntax across MySQL, PostgreSQL, and SQLite: keyword casing, JOIN layout, subquery nesting. Dialect-specific constructs (PostgreSQL :: casts, window functions) follow the generic rules — layout only, never semantics.
Is uppercase keyword styling required?
No — it is a readability convention. SQL is case-insensitive, but uppercase keywords visually separate from lowercase identifiers, which pays off in long queries. The one-click uppercase option exists to enforce exactly this convention.

Content reviewed: