SQL Formatter

Format SQL queries across major dialects — PostgreSQL, MySQL, Snowflake, T-SQL, BigQuery, SQLite, and more. Keyword casing and indentation are configurable.

Loading…

All processing runs in your browser — no files or inputs are uploaded to a server.

How to use

Paste a SQL statement on the left, and the right pane shows it re-indented with one clause per line, joins aligned, and lists broken across lines for readability. Pick the dialect to match your target database (PostgreSQL, MySQL, Snowflake, T-SQL, BigQuery, SQLite, and 8 more) — dialect choice affects how reserved words, quoted identifiers, and dialect-specific clauses (`QUALIFY`, `RETURNING`, `EXCEPT`) are recognized. Keyword case can be set to UPPER (SQL community default), lower, or preserve, and tab width adjusts indent depth between 1 and 8 spaces.

The formatter parses the statement into a clause tree before re-emitting — it is not a regex-based pretty-printer, so a comma split across joins or an inline CASE expression comes out structured rather than just wrapped. It does *not* validate the query against a schema and does *not* execute it. Everything runs in the browser through the `sql-formatter` library, so queries containing internal table names or production data stay on your machine.

Examples

Untangle a single-line ad-hoc query

Input
select id, name, email from users where created_at > '2024-01-01' and status in ('active','pending') order by created_at desc limit 100
Output
SELECT
  id,
  name,
  email
FROM
  users
WHERE
  created_at > '2024-01-01'
  AND status IN ('active', 'pending')
ORDER BY
  created_at DESC
LIMIT
  100

Each clause becomes its own header line and the column list breaks one per line. The result diffs cleanly when columns are added or reordered — easier to spot the change in a PR than a single-line query.

Format a complex JOIN with CTEs (PostgreSQL)

Input
with active_users as (select id from users where status='active') select o.id, o.total, u.email from orders o join active_users au on o.user_id=au.id join users u on u.id=au.id where o.created_at >= now() - interval '7 days' returning *
Output
WITH active_users AS (
  SELECT
    id
  FROM
    users
  WHERE
    status = 'active'
)
SELECT
  o.id,
  o.total,
  u.email
FROM
  orders o
  JOIN active_users au ON o.user_id = au.id
  JOIN users u ON u.id = au.id
WHERE
  o.created_at >= now() - INTERVAL '7 days'
RETURNING
  *

CTE body is nested and joins line up under FROM with their conditions inline. The PostgreSQL dialect is needed because of `RETURNING` and `INTERVAL '7 days'` syntax — Standard SQL mode does not know those.

Dialect mismatch — when the wrong choice breaks parsing

Input
SELECT * FROM events QUALIFY ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY ts DESC) = 1
Output
(parses cleanly with Snowflake or BigQuery dialect; loses structure with PostgreSQL since it has no QUALIFY clause)

`QUALIFY` filters rows after a window function and exists in Snowflake, BigQuery, Databricks, and Teradata — but not in PostgreSQL, MySQL, or SQLite. Pick the dialect that ships the clause, otherwise the formatter sees `QUALIFY` as an unknown identifier and falls back to flatter output.

FAQ

Does the formatter execute or validate my query?

No — it only re-prints the syntax. The parser checks that tokens form a recognizable shape, but it does not connect to a database, look up tables, or check column existence. A query that formats cleanly here might still fail at runtime because of typos in table names, missing privileges, or schema drift.

Why uppercase keywords by default?

It is the long-standing SQL community convention — uppercase reserved words make clause structure visible against lowercase identifiers, even on syntax-coloring-free terminals and grep output. Style guides from Mode, Holistics, and the SQL Style Guide all recommend it. Switch to `lower` if your team prefers Python-style lowercase or to `preserve` to leave the input alone.

Which dialect should I pick if I am not sure?

Match the database the query will run against. If unknown, PostgreSQL is the safest middle ground — it accepts a broad superset of Standard SQL including window functions, CTEs, `RETURNING`, and JSON operators, and most queries written for other engines parse cleanly under it. For BigQuery, Snowflake, or T-SQL, pick those explicitly because their dialect-specific clauses (`QUALIFY`, `EXCEPT` for differences, `TOP n`) need the matching parser.

Why are tabs disabled — can I export with `\t` indentation?

The tool forces spaces because most code review tools, diff viewers, and shared environments render tabs unpredictably (4 spaces in one editor, 8 in another). If your style guide insists on tabs, post-process the output with `expand -t1 | sed 's/ /\t/g'` or a single editor find-replace. Production SQL files seen in the wild use spaces almost universally.

Can I keep comments inside the query?

Yes. Both line comments (`--`) and block comments (`/* … */`) are preserved at their original position. They survive the parse/emit round-trip, which means you can run the formatter on production queries without losing inline documentation. Hint syntax like `/*+ INDEX(t i_t_a) */` (Oracle) and `/*! STRAIGHT_JOIN */` (MySQL) is preserved as a normal comment block.

Format on save in my editor — should I use this tool or a CLI?

Use a CLI for editor integration — `pg_format`, `sqlfluff format`, or the `sql-formatter` package itself via Prettier plugin. This web tool is for one-off pastes (a colleague's ad-hoc query, a screenshot you transcribed, a quick check before opening a ticket) where loading a project just to format ten lines is overkill. Both share the same formatting library so the output is consistent.

Related concepts

SQL was first standardized in 1986 (ANSI SQL-86) and is now in revision SQL:2023, but every major engine carries its own dialect on top of the standard. The differences are not cosmetic: a query that runs on Snowflake may not parse on PostgreSQL because of `QUALIFY`, `LATERAL FLATTEN`, or `:` variable substitution; a T-SQL batch with `GO` separators and `DECLARE @x` is meaningless to MySQL. A dialect-aware formatter parses the query against the chosen grammar and re-prints it, preserving constructs the standard does not know.

Formatting is one of three nearby disciplines. **Formatting** restructures whitespace and case while keeping the query semantically identical — this tool. **Linting** flags style or correctness smells (`sqlfluff`, `sqlint`, the Snowflake `dbt` linter) such as unqualified `*`, unused CTEs, or implicit type coercions. **Static analysis** goes further (`squawk`, `eversql`, `pganalyze`) to read query plans and warn about missing indexes or table-locking DDL. Formatters are safe to run automatically on every commit; linters fit pre-commit hooks; analyzers usually run against a real planner.

Three adjacent SQL writing conventions are worth recognizing. **Leading commas** (`, column_a` instead of `column_a,`) make adding lines diff-friendly and are used by Mode, Hashicorp, and several data team style guides — `sql-formatter` does not enforce this. **River formatting** vertically aligns clause keywords (`SELECT`, `FROM`, `WHERE` all right-aligned to a common column) — common in older Oracle shops and rejected by most modern guides for being hard to maintain. **Stack formatting** is what this tool produces by default — clause header on its own line, contents indented below. Pick the style your team reads fastest in pull requests, then let the formatter enforce it.

Related tools