SQL Formatter
Format and beautify SQL for PostgreSQL, MySQL and SQL Server — clauses on their own lines, subqueries indented, literals untouched.
SQL
Double-quoted identifiers, $1 parametersFormatted
Paste a query to format it.
Formatting runs in your browser — queries, table names and any literal values in them are never uploaded. Only whitespace and keyword casing change; string literals, comments and quoted identifiers are passed through untouched.
About the SQL Formatter
Paste a query — including the single unbroken line your ORM or slow-query log produced — and get it back laid out so a reviewer can read it. Clauses start their own lines, selected columns stack, joins keep their ON condition alongside them, and subqueries indent under the clause that owns them.
The formatter tokenises the query before touching it. That matters more than it sounds: string literals, comments and quoted identifiers are captured whole, so a `--` inside a string is never mistaken for a comment and the word `select` inside a literal is never uppercased. Only whitespace and the case of actual keywords change; every character of your data is passed through untouched.
Not everything gets expanded. A subquery or a CASE expression is worth breaking across lines; `count(*)`, `coalesce(a, b)` and `IN (1, 2, 3)` are not, and exploding them makes a query harder to read rather than easier. The formatter decides per parenthesis by looking at what is inside it. It also distinguishes `users (id, name)` from `count(id)` using the spacing in your original — the only reliable signal available without a full parser.
Formatting is idempotent: running it on already-formatted output returns the same text. That is what makes it safe to put in a pre-commit hook or apply to a file you have checked in.
- PostgreSQL, MySQL and SQL Server identifier quoting — double quotes, backticks and brackets
- Keyword casing set to upper, lower or preserved, applied only to real keywords
- Subqueries and CTEs indented under their owning clause; function calls left inline
- CASE expressions broken across WHEN, ELSE and END
- Parameter placeholders recognised in all four styles: $1, ?, :name and @name
How to use it
- Paste your query into the left pane — one long line is exactly the case this is for.
- Pick your dialect. It sets the identifier-quoting hint; all three quoting styles are accepted regardless, so a query pasted from elsewhere still formats.
- Choose keyword casing and indent width to match your team's conventions.
- Turn off One column per line for short queries you would rather keep compact.
- Copy the result, or press Replace input to format again on top of it.
Real-world use cases
Backend & API developers
Paste the one-line query your ORM logged or a slow-query log captured and get it back readable enough to actually review, without retyping it by hand.
Database administrators & data engineers
Run a migration file through this SQL formatter before it goes into a PR, so every statement in the repo follows the same keyword casing and indent width regardless of who wrote it.
Code reviewers
Normalize two queries to the same style before diffing them, so the diff shows the actual logic change instead of a wall of whitespace noise from a different editor's auto-format.
Analytics & data engineers
Clean up a sprawling ad-hoc query pulled from a BI tool or a notebook — CTEs, subqueries and joins indented consistently — before handing it to a teammate.
DevOps & platform engineers
Wire the same formatting into a pre-commit hook or CI check: the engine behind this page is a pure, idempotent function, so formatted SQL never drifts once it is checked in.
Examples
A one-line query from a log
select id, name, email from users where active = true and created_at > '2026-01-01' order by name asc limit 10;
SELECT id, name, email FROM users WHERE active = TRUE AND created_at > '2026-01-01' ORDER BY name ASC LIMIT 10;
AND starts its own line so each condition can be read and commented on separately. LIMIT keeps its value alongside it — breaking that pair helps nobody.
A join with an aggregate
SELECT u.id, u.name, count(o.id) AS order_count FROM users u LEFT JOIN orders o ON o.user_id = u.id GROUP BY u.id, u.name HAVING count(o.id) > 3;
SELECT u.id, u.name, count(o.id) AS order_count FROM users u LEFT JOIN orders o ON o.user_id = u.id GROUP BY u.id, u.name HAVING count(o.id) > 3;
The join condition stays on the join line, which is what makes a multi-join query scannable. count(o.id) is left inline rather than expanded.
A subquery
select * from users where id in (select user_id from orders where total > 100) and name like 'A%';
SELECT
*
FROM
users
WHERE
id IN (
SELECT
user_id
FROM
orders
WHERE
total > 100
)
AND name LIKE 'A%';The inner query is indented under the condition that contains it, and the outer AND resumes at its original level after the closing parenthesis.
Literals are never touched
select 'it''s a -- test', "select" from t where x = '/* not a comment */';
SELECT 'it''s a -- test', "select" FROM t WHERE x = '/* not a comment */';
The doubled quote, the -- inside a string and the keyword used as a quoted identifier all survive. A regex-based formatter mangles all three.
Common errors
| Message | Cause | Fix |
|---|---|---|
| Formatted output looks wrong after a vendor-specific construct | This is a formatter, not a full parser. An unusual dialect extension may not match any known clause and is passed through as plain tokens. | The query is still semantically unchanged — only whitespace moved. Adjust by hand, or leave that statement unformatted. |
| A table name lost the space before its column list | The original had no space — `insert into users(id)` — so it is indistinguishable from a function call. | Add a space in the source: `insert into users (id)`. The spacing you write is the only signal available here. |
| Keywords in a comment were not uppercased | Intentional. Comments are captured whole and passed through byte for byte. | Edit the comment yourself if you want it changed. |
| syntax error at or near ... after formatting | Almost always a query that was already invalid, now easier to see. Formatting only moves whitespace. | Compare against the original. If the two genuinely differ in more than whitespace, the input contained an unterminated string or comment. |
| Very long query is slow to reformat as you type | The result is recomputed on every keystroke. | Paste the whole query at once rather than typing it. Formatting is a single pass and completes quickly even for large statements. |
Frequently asked questions
›Is my query sent to a server?
No. Tokenising and formatting both run in your browser. Table names, column names and any literal values embedded in the query — including anything from a production log — never leave the page, and nothing is stored or logged.
›Does formatting change what the query does?
No. Only whitespace between tokens and the case of recognised keywords change, and SQL keywords are case-insensitive. String literals, comments and quoted identifiers are passed through unmodified, which is the part that matters — in PostgreSQL a quoted identifier is case-sensitive, so altering it would change meaning.
›Why are some parentheses expanded and others not?
Because expanding all of them makes queries worse. A parenthesis containing SELECT or WITH holds a subquery and is broken across lines; everything else — function arguments, value lists, arithmetic grouping — stays inline. The decision is made per parenthesis by looking at its contents.
›What does the dialect setting change?
It sets the identifier-quoting and parameter-style hint shown in the editor. All three quoting styles and all four parameter styles are tokenised regardless of the setting, so a query pasted from a different database still formats correctly rather than failing.
›Can I run this in a pre-commit hook?
The engine behind this page is a pure function with no dependencies, and formatting is idempotent — running it on its own output returns identical text. That is the property a hook needs. The page itself is browser-only, so you would need to call the underlying formatter from your own script.
›Should keywords be uppercase?
It is purely convention; SQL does not care. Uppercase keywords are the older and more widespread style and make the shape of a long query easy to scan. Lowercase is increasingly common in codebases where SQL sits alongside application code. Pick one and apply it consistently — which is what the option is for.
›Does it validate my SQL?
No. It tokenises rather than parses, so it will happily format a query with a genuine syntax error. Use your database's own EXPLAIN or a linter for validation; this tool's job is readability.
›Does it handle CTEs and window functions?
WITH starts its own line the same way SELECT or FROM does, so a CTE reads as its own block rather than sitting inline. OVER, PARTITION BY and the other window-function keywords are recognised and cased correctly, but the window frame inside the parentheses is left as one of the parenthesis cases the formatter decides not to expand — the same rule that keeps count(*) inline. For a query built mostly of CTEs, that keeps the top-level structure readable without over-indenting.
Last updated