SQL Formatter

Turn a one-line query from a log, ORM debug output or a colleague’s message into readable, consistently indented SQL.

Input

Settings

History

Load from URL

What gets formatted

SQL arrives unreadable in predictable places: Hibernate and ActiveRecord logs, slow-query logs, EXPLAIN output, BI tools that generate 300-character SELECT lists, and migration files nobody has touched since they were written. This page lays out SELECT, INSERT, UPDATE, DELETE, MERGE, DDL and CTEs one clause per line, indents subqueries and CASE expressions, and keeps short parenthesised expressions on one line.

Layout is produced by the open-source sql-formatter library. Before it runs, PasteKit’s own tokenizer checks the input for the mistakes that most often break a query in transit: a string literal with no closing quote, an unclosed /* comment, and unbalanced parentheses. These are reported with the exact line and column rather than as a vague parse failure further down.

The checker is lexical, not a database. It will not tell you that a column does not exist, and an incomplete query such as SELECT id FROM orders WHERE still formats. Treat it as a syntax sanity check, not a substitute for running the query.

Choose the right dialect

Databases disagree about basic lexical rules, so the Dialect option matters more than it looks:

  • Backticks quote identifiers in MySQL, MariaDB, BigQuery and Spark, but are not valid in SQL Server, where [brackets] are used.
  • 'it\'s' is a valid string in MySQL, while PostgreSQL and SQL Server only accept the doubled form 'it''s'.
  • $1, ?, :name and @name placeholders belong to different drivers and databases.
  • PostgreSQL and Snowflake allow dollar-quoted bodies ($$ ... $$); Oracle has q'[...]' strings.

With Standard SQL selected, anything outside the ANSI core, such as @@version or [dbo].[orders], is reported as “not valid Standard SQL syntax” with a hint to choose another dialect. Each database has its own page with specifics: MySQL, PostgreSQL, SQL Server T-SQL, Oracle PL/SQL, SQLite, BigQuery and Snowflake, among others.

Style options

  • Keyword case: UPPER (SELECT), lower or As written. Data types follow the same setting.
  • Function case: As written, UPPER (COUNT(*)) or lower. Kept separate because many teams uppercase keywords but not functions.
  • Indent style: Standard puts each clause on its own line with the body indented below it. Tabular, left-aligned and Tabular, right-aligned put the clause keyword in a fixed-width column with its content beside it, the “river” style some SQL style guides prefer.
  • Commas: Trailing or Leading (, customer_id at the start of each line). Leading commas make it easy to comment out the last column; sql-formatter itself has no such option, so PasteKit rewrites the output without touching commas inside strings or comments.
  • AND / OR: put logical operators at the Start of line or End of line.
  • Blank lines between statements: 0 to 5 empty lines between queries in a script.

Indent size and line width are shared with other formats; the line width also decides how long a parenthesised expression can be before it is broken across lines. For conventions behind these choices, see the SQL style guide.

Keys and minifying

Ctrl/Cmd+Enter formats and Ctrl/Cmd+Shift+M minifies: comments and line breaks are removed, but strings, quoted identifiers, optimizer hints (/*+ ... */) and MySQL /*! ... */ comments are kept. Ctrl/Cmd+Shift+C copies, and Ctrl/Cmd+K opens the command palette. Queries copied from production logs can contain customer emails and IDs; the formatter runs inside your browser, so they stay with you.

Examples

Revenue report from an ORM log

A typical generated query gets one clause per line, with the WHERE conditions split at AND.

Input
select c.id, c.name, count(distinct o.id) as orders, sum(i.qty * i.unit_price) as revenue from customers c join orders o on o.customer_id = c.id join order_items i on i.order_id = o.id where o.status in ('paid', 'shipped') and o.placed_at >= '2026-07-01' and o.placed_at < '2026-10-01' group by c.id, c.name having sum(i.qty * i.unit_price) > 500 order by revenue desc limit 25;
Output
SELECT
  c.id,
  c.name,
  count(DISTINCT o.id) AS orders,
  sum(i.qty * i.unit_price) AS revenue
FROM
  customers c
  JOIN orders o ON o.customer_id = c.id
  JOIN order_items i ON i.order_id = o.id
WHERE
  o.status IN ('paid', 'shipped')
  AND o.placed_at >= '2026-07-01'
  AND o.placed_at < '2026-10-01'
GROUP BY
  c.id,
  c.name
HAVING
  sum(i.qty * i.unit_price) > 500
ORDER BY
  revenue DESC
LIMIT
  25;
Open this example in the tool

CTE with leading commas and tabular layout

Each CTE is indented as its own query, column lists start with commas, and functions such as RANK() are uppercased.

Input
with monthly as (select date_trunc('month', placed_at) as month, customer_id, sum(total) as spend from orders where status = 'paid' group by 1, 2), ranked as (select month, customer_id, spend, rank() over (partition by month order by spend desc) as rnk from monthly) select month, customer_id, spend from ranked where rnk <= 3 order by month, rnk;
Output
WITH      monthly AS (
          SELECT    date_trunc ('month', placed_at) AS MONTH
                  , customer_id
                  , SUM(total) AS spend
          FROM      orders
          WHERE     status = 'paid'
          GROUP BY  1
                  , 2
          )
        , ranked AS (
          SELECT    MONTH
                  , customer_id
                  , spend
                  , RANK() OVER (
                    PARTITION BY MONTH
                    ORDER BY  spend DESC
                    ) AS rnk
          FROM      monthly
          )
SELECT    MONTH
        , customer_id
        , spend
FROM      ranked
WHERE     rnk <= 3
ORDER BY  MONTH
        , rnk;
Open this example in the tool

Migration script with several statements

Three statements on one line are split apart with two blank lines between them and keywords in lower case.

Input
ALTER TABLE orders ADD COLUMN refunded_at TIMESTAMP NULL; UPDATE orders SET refunded_at = updated_at WHERE status = 'refunded'; CREATE INDEX idx_orders_refunded_at ON orders (refunded_at);
Output
alter table orders
add column refunded_at timestamp null;


update orders
set
  refunded_at = updated_at
where
  status = 'refunded';


create INDEX idx_orders_refunded_at on orders (refunded_at);
Open this example in the tool

Common errors and how to fix them

ErrorCauseFix
This string is never closed — the ' has no matching '
Explained
A quote inside a value, such as O’Brien, ended the string early, or the query was cut off mid-literal.Double the inner quote (‘O’‘Brien’). In MySQL, ' also works if you choose the MySQL dialect.
This '(' is never closed
Explained
A subquery, function call or IN list is missing its closing parenthesis, often after editing a long WHERE clause.Add the missing ) where the expression ends; the position points at the opening bracket that has no partner.
Unexpected ')' — there is no matching '(' before it
Explained
An extra closing parenthesis, frequently left behind after deleting a function wrapper such as COALESCE(.Remove the stray ) or restore the opening part of the expression.
"[id]" is not valid Standard SQL syntaxThe query uses syntax from a specific database (here SQL Server brackets) while the Dialect is set to Standard SQL.Pick the matching Dialect, for example T-SQL (SQL Server); the tokenizer then understands its quoting and variables.
This /* comment is never closedA block comment has no */, so everything after it would be ignored by the database too.Close the comment where it should end.

Frequently asked questions

Does the SQL formatter change what my query does?

No. Only whitespace, line breaks and the case of keywords and functions change. Strings, quoted identifiers and comments are left as they are, so the formatted query runs exactly like the original.

How do I format SQL in SQL Server Management Studio or DBeaver?

DBeaver has Format > Format SQL (Ctrl+Shift+F). SSMS has no built-in formatter, so people use extensions or paste into a tool like this one with the T-SQL dialect selected.

Can it validate my SQL?

It catches lexical problems (unclosed strings and comments, unbalanced brackets) and syntax the chosen dialect does not recognise. It does not know your schema, so a misspelt table or column passes; run EXPLAIN against a test database for that.

Is my query sent to a server?

No. Formatting runs in your browser, so production queries with real customer values never leave your machine.

Should SQL keywords be uppercase?

Databases do not care. Uppercase keywords remain the most common convention because they separate keywords from identifiers at a glance, but lowercase is popular in dbt and analytics code; pick one and apply it consistently.

Related tools