
SQL Formatter
Beautify and format SQL queries with proper indentation and keyword casing. 100% client-side.
Last reviewed: April 2026New to this tool? Click here for instructions
Paste any SQL query — from a single SELECT to a thousand-line CTE chain — and get back consistently formatted, indented, dialect-aware SQL. Everything runs client-side; your queries never leave the browser.
What This Tool Does
This tool takes raw, often single-line SQL pasted from a log file, an ORM debug print, a Slack message, or a hastily-typed editor buffer, and restructures it into a readable, consistently indented form. It splits major clauses (SELECT, FROM, JOIN, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT) onto their own lines, indents the column list following SELECT, lines up ON conditions with their respective JOINs, and stacks boolean predicates inside WHERE so each AND/OR clause starts on a fresh line. The result is SQL you can read top-to-bottom like a sentence rather than scanning a horizontal wall of text.
The formatter supports the dialects you actually encounter in production environments: MySQL, PostgreSQL, Microsoft SQL Server (T-SQL), Oracle PL/SQL, SQLite, Google BigQuery, and Amazon Redshift. Dialect-specific syntax — PostgreSQL's RETURNING clause, MySQL's backtick-quoted identifiers, SQL Server's [bracket] identifiers, Oracle's DUAL pseudo-table, BigQuery's STRUCT and ARRAY constructors — is recognized and preserved verbatim. Keyword casing is configurable (UPPER, lower, or as-typed), indent width can be set to 2 spaces, 4 spaces, or tabs, and a Compact mode strips line breaks for embedding SQL in JSON payloads or commit messages where space matters.
Every transformation runs in your browser via JavaScript. The query is never uploaded, never logged, and never seen by any server — relevant when you're formatting SQL that contains schema details, real customer data in WHERE clauses, or proprietary business logic. 🔒
How to Use It: Step-by-Step
The interface is built for paste-and-go use, but each option meaningfully changes the output, so it pays to know what each toggle does before you reach for it.
1. Paste Your Query
Drop your SQL into the input pane on the left (or the top pane on mobile). The tool accepts queries of any length — a single SELECT, an entire stored procedure body, or a multi-statement script separated by semicolons. The input pane accepts Tab without losing focus, so you can paste in indented snippets without the cursor jumping out.
2. Pick a Dialect (Optional)
For the great majority of queries the dialect setting is invisible — the SELECT/FROM/WHERE skeleton is identical across every major engine. Where dialect matters: how identifiers are quoted (backticks in MySQL, double quotes in PostgreSQL, square brackets in SQL Server), how Oracle handles DUAL, how BigQuery handles STRUCT field access, and how PostgreSQL's RETURNING clause attaches to INSERT and UPDATE. Choose the dialect that matches your target engine for the cleanest result.
3. Choose Keyword Case
UPPER is the dominant convention in published style guides (Mozilla, Holywell, Stripe, GitLab, Mode Analytics) and makes clause boundaries scannable at a glance. lower is increasingly popular in modern dbt-style projects, where uppercase is reserved for SQL identifiers and constants. PascalCase exists primarily for legacy SQL Server codebases that historically capitalized keywords as a stylistic flourish. Pick one per project and stay consistent — the worst outcome is mixed casing across a single codebase.
4. Set Indent Style
Two spaces is the most common default in formatter libraries (it matches the JavaScript and Python communities). Four spaces produces deeper visual nesting that some teams prefer for highly nested CTEs. Tabs render differently across editors but are friendlier to accessibility tooling because each developer can configure their preferred visual width independently.
5. Read the Output, Copy, or Download
The formatted query appears in the right pane (or below the input on mobile). Click Copy to drop it on your clipboard or Download to save it as a .sql file. The status bar below the panes shows live stats — line count, keyword count, and any tokenization warnings — so you have immediate confidence the formatter parsed your query correctly.
Worked Example: A 200-Character One-Liner Becomes 30 Readable Lines
Here is exactly the kind of query that arrives in a debug log from an ORM or from copy-pasting out of a SQL editor history. It contains a CTE, two JOINs, a WHERE clause with three predicates, a GROUP BY, a HAVING filter, and an ORDER BY with a LIMIT. On one line it is essentially unreviewable.
Before: Single Line, 197 Characters
WITH active_users AS (SELECT id, email FROM users WHERE deleted_at IS NULL AND last_login_at > NOW() - INTERVAL '30 days') SELECT au.email, COUNT(o.id) AS order_count, SUM(o.total) AS revenue FROM active_users au INNER JOIN orders o ON au.id = o.user_id LEFT JOIN refunds r ON o.id = r.order_id WHERE o.status = 'completed' AND r.id IS NULL AND o.created_at > '2026-01-01' GROUP BY au.email HAVING SUM(o.total) > 500 ORDER BY revenue DESC LIMIT 25;
After: 30 Lines, Standard Mode, UPPER Keywords, 2-Space Indent
WITH active_users AS (
SELECT
id,
email
FROM users
WHERE deleted_at IS NULL
AND last_login_at > NOW() - INTERVAL '30 days'
)
SELECT
au.email,
COUNT(o.id) AS order_count,
SUM(o.total) AS revenue
FROM active_users au
INNER JOIN orders o
ON au.id = o.user_id
LEFT JOIN refunds r
ON o.id = r.order_id
WHERE o.status = 'completed'
AND r.id IS NULL
AND o.created_at > '2026-01-01'
GROUP BY au.email
HAVING SUM(o.total) > 500
ORDER BY revenue DESC
LIMIT 25;
Three things to notice in the output. First, the CTE body is itself indented as if it were a nested query — readers can trace its boundaries without counting parentheses. Second, the ON condition for each JOIN sits on the line directly under its JOIN keyword, indented one level, so a reader scanning JOINs sees a clear hierarchy. Third, every AND in the WHERE clause starts a new line aligned with the first predicate, making it impossible to miss a clause hiding at the end of a long line. The query semantically identical to the input; only whitespace changed. Run an EXPLAIN on both and the planner output will be byte-identical.
Common Use Cases
Code Review and Pull Request Diffs
Reviewing a SQL diff in a pull request is painful when the query is on one line — every character change shifts the rest of the line and the diff highlights become noise. Formatted queries diff cleanly: changing WHERE status = 'completed' to WHERE status IN ('completed', 'partial') shows as a single-line modification rather than a wall of red and green. Format your SQL before committing and your reviewer's job becomes review rather than archaeology.
Embedding SQL in Commit Messages and Documentation
Compact mode strips redundant whitespace down to a single space between tokens, useful when you want to include the actual query in a commit message body, a Slack notification, or a JSON API payload where line breaks would inflate payload size. Standard mode is what you want in Markdown documentation, runbooks, and architecture docs — embedded in a fenced code block, a formatted query reads naturally alongside prose explaining its intent.
Debugging Dynamically-Built SQL
ORMs and query builders generate single-line SQL for transmission to the database. When you log that SQL for debugging — Django's django.db.connection.queries, Rails' development.log, SQLAlchemy's echo mode — you get a wall of unformatted text that's almost impossible to read. Paste it through the formatter and the structure leaps out: missing JOINs become obvious, accidentally-cartesian products become obvious, missing WHERE clauses on UPDATE statements become alarmingly obvious.
Sharing Queries on Slack, Stack Overflow, and GitHub Issues
A formatted query in a GitHub issue or Stack Overflow question gets answered faster than an unformatted one. Readers can mentally parse the structure without doing it themselves, which lowers the friction to engagement. The same applies internally: a formatted query in a Slack channel signals that you've put thought into the problem and gets faster, better responses than a one-liner ever will.
Generating Documentation from Live Code
If your project documents its data model with example queries — common in analytics handbooks, dbt project docs, or internal "how do I get X" wikis — running every snippet through the formatter before publishing produces a consistent visual style across the entire corpus. New examples drop in feeling like they belong, rather than each example carrying the formatting style of whoever wrote it.
Learning SQL Formatting Conventions
Engineers new to SQL often write it the way they write JavaScript or Python — squashed against the left margin, occasional line breaks. Reformatting their queries through a real formatter teaches the conventions by example: where line breaks go, how JOIN/ON pairs align, how column lists indent under SELECT. Within a few weeks the formatter becomes optional because the conventions are internalized.
Edge Cases and Limitations
SQL is grammatically far more context-sensitive than people assume, and any formatter that wants to be useful has to handle several categories of weirdness gracefully.
String Literals Are Sacred
The string 'SELECT * FROM users' is a literal value — not a query — and must not have its SELECT or FROM uppercased, line-broken, or otherwise touched. The tokenizer scans for single quotes and consumes everything up to the closing quote verbatim, including SQL keywords, comment markers, semicolons, and parentheses. The standard SQL-92 escape convention for embedded apostrophes is doubling ('It''s' for the string It's), and the tokenizer recognizes this pattern as an escaped quote rather than a string terminator. Inside a string, nothing changes.
Line Comments and Block Comments
Single-line comments starting with -- and block comments delimited by /* ... */ are preserved character-for-character. Block comments can span multiple lines and the formatter does not re-flow them. Comments attached to specific lines (a trailing -- TODO: index this column) stay attached to the same logical position after formatting, so reviewers don't lose contextual annotations. Comments inside SELECT column lists are kept aligned with the column they follow.
Dialect-Specific Identifier Quoting
PostgreSQL uses double-quoted identifiers ("My Column"), MySQL uses backticks (`my_column`), SQL Server uses square brackets ([my column]), and Oracle accepts either double quotes or unquoted. Inside any of these quoting schemes, the content is treated as an identifier — never a keyword — even if the content happens to match a keyword name. SELECT "select" FROM "table" is a perfectly valid (if maddening) PostgreSQL query, and the formatter passes the quoted identifiers through unchanged.
Oracle's DUAL and Other Dialect Pseudo-Tables
Oracle's DUAL is a one-row, one-column table used as a syntactic anchor: SELECT SYSDATE FROM DUAL. It is neither a keyword nor a user table — it's a built-in pseudo-table. The formatter treats it as an identifier so it remains usable regardless of keyword-case mode. Similar handling applies to SQL Server's sys.tables, PostgreSQL's pg_catalog, and BigQuery's INFORMATION_SCHEMA — system identifiers preserved as-is.
CTEs and Recursive Queries
Common Table Expressions introduced with WITH are formatted with their body indented one level, making the CTE feel like a named local function. WITH RECURSIVE queries — used for traversing trees, graphs, and bill-of-materials structures — format identically; the recursion is in the query semantics, not the syntax. Multiple CTEs in a single statement are formatted with each CTE on its own block, separated by commas at the end of the preceding closing parenthesis line.
Window Functions and OVER Clauses
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) is formatted with the OVER clause on the same line as its function call unless the OVER clause is long enough to wrap. Window function specifications get the standard PARTITION BY / ORDER BY / ROWS BETWEEN structure on their own lines when present — the same shape as the main query's clauses, just inside parentheses.
JSON Path Operators
PostgreSQL's -> and ->> JSON path operators, MySQL's JSON_EXTRACT shorthand -> and ->>, and SQL Server's JSON_VALUE calls are recognized as operators, not as parts of identifiers or comments. They are formatted inline alongside the surrounding expression rather than triggering a line break.
Behind the Scenes: How SQL Formatters Actually Work
SQL Is Context-Sensitive, Not Just Context-Free
Most programming languages — JavaScript, Python, Go — are parsed using context-free grammars where a token's role is determined entirely by the token itself. SQL is different. Whether count is a column name or the COUNT aggregate function depends on whether it's followed by an opening parenthesis. Whether order is a table name or part of the ORDER BY clause depends on whether BY follows it. The SQL:2016 standard formalizes this as syntax with "reserved" and "non-reserved" keywords — non-reserved keywords (like count and order) can be used as identifiers in many contexts, but only the surrounding grammar reveals which interpretation applies. A correct formatter must therefore do at least minimal grammatical analysis, not just regex-based token replacement.
The SQL:2016 Standard and ANSI Compliance
The SQL standard is maintained jointly by ISO and ANSI; the current version is SQL:2023, with SQL:2016 being the version most widely supported across engines. The standard runs to several thousand pages and is divided into parts (Foundation, Information Schema, Persistent Stored Modules, Multimedia, OLAP, XML, JSON, Property Graph Queries). No major engine implements all of it; every engine implements a different subset plus engine-specific extensions. This is why dialect awareness matters: RETURNING is standard in DB2 and PostgreSQL but missing in MySQL and SQL Server, and FETCH FIRST n ROWS ONLY is the standard pagination syntax while LIMIT n is the de-facto standard practiced by everyone except Oracle and SQL Server.
ANTLR Grammars and Why People Use Them
ANTLR is the most widely used parser generator for SQL dialect implementations. The Presto/Trino, Apache Spark, and Apache Calcite projects all maintain ANTLR grammars for their SQL dialects, totaling tens of thousands of grammar lines. A full ANTLR-based formatter parses the input into an AST, then walks the AST emitting formatted output — this approach handles every grammatical edge case correctly but is heavy: a JavaScript port of an ANTLR SQL grammar can easily weigh 500KB+ before minification. For a browser-based formatter, that's prohibitive.
The sql-formatter NPM Library
The sql-formatter npm library — currently around 350KB minified — uses a hand-written tokenizer plus a small clause-aware formatter. It's the most popular open-source SQL formatter for JavaScript and supports MySQL, PostgreSQL, SQL Server (T-SQL), Oracle PL/SQL, SQLite, BigQuery, Redshift, and DB2 dialects. Its approach is closer to "split on keywords with context awareness" than to full AST parsing, which trades some grammatical precision for dramatically smaller bundle size and faster execution.
Two Philosophies: Split-on-Keywords vs. AST-Based
Split-on-keywords formatters tokenize the query, scan for major clause keywords, and insert line breaks and indentation at known positions. They're fast, small, and handle the vast majority of real-world SQL correctly. They struggle with edge cases where a keyword appears in an unexpected context — for instance, when order is used as an unquoted column name. AST-based formatters first parse the input into a full abstract syntax tree, then emit formatted output from the tree. They handle every edge case correctly because they understand the grammar, but they are large (because the grammar is large) and slow (because parsing is slow). This tool uses the split-on-keywords approach with dialect-aware quote handling and string-literal protection — the practical sweet spot for browser-based formatting.
Comparison: This Tool vs. DataGrip vs. pgFormatter vs. sqlfluff vs. DBeaver
Several mature formatters exist; each makes different trade-offs. The comparison below covers when each tool is the right choice.
| Tool | Type | Cost | Dialects | Linting | When to Use |
|---|---|---|---|---|---|
| This Tool | Browser-based | Free | MySQL, PostgreSQL, T-SQL, Oracle, SQLite, BigQuery, Redshift | No | One-off queries from logs, Slack pastes, code review prep, learning SQL formatting conventions |
| DataGrip (built-in) | IDE plugin | $229/yr individual | All major dialects via JetBrains' shared SQL parser | Yes (warnings and intentions) | Daily-driver SQL development with full IDE features (refactoring, navigation, dialect-aware autocomplete) |
| pgFormatter (Perl) | CLI / web demo | Free (PostgreSQL License) | PostgreSQL-focused, handles others | No | Batch-formatting PostgreSQL files in CI, scripting through a Perl pipeline |
| SQLFluff | Python CLI + pre-commit | Free (MIT) | BigQuery, ClickHouse, Databricks, DB2, Hive, MySQL, Oracle, PostgreSQL, Redshift, Snowflake, SOQL, SparkSQL, SQLite, T-SQL, TeradataSQL, Trino | Yes (style rules + auto-fix) | CI/CD enforcement of SQL style across a team or repo; dbt projects; pre-commit hooks |
| DBeaver (built-in) | IDE | Free (CE) / $99/yr (PRO) | 80+ databases | Limited | Multi-database development, free alternative to DataGrip, ad-hoc database administration |
Use this tool when you have an ad-hoc one-liner from a log or a Slack message and you want the formatted version in your clipboard in five seconds without launching a heavyweight IDE. Use DataGrip when you live in JetBrains all day and SQL is your daily-driver workload — its dialect awareness extends to every other SQL feature (autocomplete, refactoring, navigation) and the integrated formatting is part of a much larger productivity surface. Use pgFormatter when you're scripting batch formatting through a Perl pipeline, especially in PostgreSQL-heavy environments. Use SQLFluff when you need enforceable style across a team — wire it into pre-commit and CI and SQL style stops being a conversation. Use DBeaver when you need a free IDE that handles dozens of databases and you can tolerate a slightly less polished SQL editor than DataGrip's.
Frequently Asked Questions
WITH clause that exists only for the duration of the surrounding statement. CTEs were introduced in SQL:1999 and are now supported in every major engine — PostgreSQL, MySQL 8+, SQL Server, Oracle, SQLite, BigQuery. They serve two purposes: breaking complex queries into named, readable building blocks, and enabling recursive queries via WITH RECURSIVE for traversing tree- or graph-shaped data such as org charts, file hierarchies, or bill-of-materials structures. CTEs improve readability without (in most engines) affecting query performance, because the planner inlines them just like a subquery.DUAL is an Oracle-specific one-row, one-column table used as a syntactic anchor when you need to select a literal value or evaluate a function without referencing a real table (SELECT SYSDATE FROM DUAL). Naive formatters sometimes lowercase it, treat it as an identifier rather than a reserved name, or break line spacing around it. This tool keeps DUAL intact regardless of keyword-case mode and treats it as an identifier so it remains usable in any Oracle context. If you see a third-party formatter mangle DUAL, switch its dialect to Oracle or PL/SQL — the issue almost always traces to dialect misconfiguration rather than a parser bug.'It''s' represents It's), which the tokenizer recognizes as an escaped quote rather than a string terminator. Keywords, comment markers, semicolons, and parentheses inside strings are preserved exactly — they are part of the string value, not SQL syntax.SELECT *, missing WHERE on UPDATE/DELETE, ambiguous column references, deprecated syntax) and flags them as warnings or errors. SQLFluff is the canonical example of a SQL linter and also functions as a formatter; this tool is purely a formatter. Use a formatter for cosmetic consistency and a linter for catching mistakes — they are complementary, not competitive.STRUCT and ARRAY, PostgreSQL JSONB operators -> and ->>, MySQL backtick-quoted identifiers, SQL Server square-bracket identifiers, Oracle PL/SQL blocks) are preserved as-is by this formatter. It does not attempt to rewrite them between dialects — that would require a full dialect-aware AST round-trip, which is what tools like sqlglot specialize in. For pure formatting (indentation, case, line breaks), this tool handles all major dialects out of the box. For dialect translation, layer a tool like sqlglot or DBeaver's SQL editor on top once your formatting is consistent.