Developer Tools

SQL Formatter & Beautifier

Format and beautify SQL queries instantly in your browser. Syntax highlighting, keyword casing, indentation control. Supports MySQL, PostgreSQL, SQLite, SQL Server and standard SQL. 100% private.

Last updated

Syntax Highlighting
Keyword Casing
Minify Option
100% Private
Our networkLegalCost.usWhat will your legal case cost?Official formulas for all 50 states. Free, no signup.Check your state
SQL
SQL Formatter & Beautifier
Keywords
Indent
Dialect
SQL Input empty
Formatted Output ready
Ready. Paste SQL on the left to format.

How the SQL Formatter Works

A SQL formatter changes only whitespace and keyword case, never what the query does. Clause keywords such as SELECT, FROM and WHERE start new lines, lists get one item per line and subqueries are indented. Paste a query above to format, highlight or minify it. String literals, quoted names and comments are kept exactly as written, and nothing leaves your browser.

The query is split into tokens first: keywords, names, numbers, operators, string literals, quoted identifiers and comments. Only the spaces and line breaks between tokens are rebuilt, so the database receives the same statement. Keyword case follows your choice; everything inside quotes is left alone.

Before and After: a Formatted Query

A one line query as it often comes out of an ORM log:

select o.id, c.name, sum(i.qty * i.price) as total from orders o join customers c on c.id = o.customer_id left join order_items i on i.order_id = o.id where o.status = 'shipped' and o.created_at >= '2026-01-01' group by o.id, c.name having sum(i.qty * i.price) > 100 order by total desc limit 20

The same query after Format SQL with the default settings (uppercase keywords, 2 space indent):

SELECT
  o.id,
  c.name,
  SUM(i.qty * i.price) AS total
FROM
  orders o
  JOIN customers c ON c.id = o.customer_id
  LEFT JOIN order_items i ON i.order_id = o.id
WHERE
  o.status = 'shipped'
  AND o.created_at >= '2026-01-01'
GROUP BY
  o.id,
  c.name
HAVING
  SUM(i.qty * i.price) > 100
ORDER BY
  total DESC
LIMIT
  20

With "Clause keywords on their own line" switched off, each keyword keeps its first item on the same line (FROM orders o), which is more compact for short queries.

What the Formatter Changes and Keeps

ElementExampleHandling
Keywordsselect, left joinUppercase, lowercase or as typed
String literals'it''s -- not a comment'Never changed
Quoted names"Order Date", `order`, [order]Never changed
Comments-- note, /* note */Kept; a line comment ends its line
Numbers1.5e3, .5, 0x1FKept as one token
Parameters$1, :name, @id, ?Kept as one token
Casts and JSON operatorstotal::numeric, data->>'id'Cast kept tight, operators spaced
Function callsCOUNT(*)No space before the parenthesis
Subqueries and CTEsIN (SELECT ...)Indented block, closing parenthesis on its own line
CASECASE WHEN ... ENDOne WHEN per line (kept inline inside function calls)

Column and table names are compared case insensitively by most databases when unquoted, but MySQL table names are case sensitive on Linux. The formatter only changes the case of words it knows as SQL keywords, and function names only when they are followed by a parenthesis, so year as a column name is left alone.

SQL Clause Order: Written vs Run

You write SELECT first, but the database logically evaluates it near the end. That order explains several common errors.

Written orderLogical orderWhat it means
SELECT5Aliases defined here do not exist yet in WHERE or GROUP BY in standard SQL
FROM / JOIN1Tables are combined first
WHERE2Filters rows; aggregates such as COUNT are not allowed here
GROUP BY3Rows are grouped
HAVING4Filters groups, so aggregates are allowed
ORDER BY6Can use SELECT aliases
LIMIT / OFFSET7Applied last

Common SQL Mistakes Formatting Makes Visible

  • A comma after the last column before FROM. With one column per line it stands out; leading commas make it impossible.
  • = NULL instead of IS NULL. A comparison with NULL is never true, so WHERE phone = NULL returns no rows.
  • NOT IN with a NULL in the list. x NOT IN (1, NULL) is never true, so the query silently returns nothing. Use NOT EXISTS instead.
  • AND and OR without parentheses. AND binds tighter, so a = 1 OR b = 2 AND c = 3 means a = 1 OR (b = 2 AND c = 3). One condition per line makes this easy to spot.
  • A filter on the right table of a LEFT JOIN placed in WHERE, which turns it into an inner join. Move the condition into the ON clause.

Formatting helps you read a query; it does not validate it. Run it against your database, or an EXPLAIN, to catch errors. For JSON columns and API payloads, the JSON formatter does the same job.

Method and sources. Token rules follow ISO/IEC 9075 (SQL standard) lexical elements plus documented dialect extensions: MySQL string literals and comment syntax, PostgreSQL dollar quoting, E strings, :: casts and JSON operators, SQL Server bracket identifiers. Logical processing order as documented for SELECT in the SQL Server and PostgreSQL manuals. The example output above was produced by running this page's formatter in Node.js.

SQL Formatter Guide

This formatter supports Standard SQL and the four most common database dialects. MySQL: backtick identifiers (`table`), LIMIT clause, AUTO_INCREMENT. PostgreSQL: double-quote identifiers ("table"), RETURNING clause, ILIKE, window functions. SQLite: minimal syntax, PRAGMA statements. SQL Server (T-SQL): square bracket identifiers ([table]), TOP instead of LIMIT, GO batch separator. The Dialect setting changes how the input is read: MySQL treats a backslash inside a string as an escape and # as the start of a comment, and PostgreSQL reads square brackets as array subscripts rather than quoted names. Every dialect accepts dollar quoted bodies ($$ ... $$) and parameters such as $1, :name, @id and ?.

SQL keywords (SELECT, FROM, WHERE, JOIN, etc.) can be written in any case: SQL is case-insensitive for keywords. The convention depends on your team's style guide: UPPERCASE is the traditional convention (most SQL books and DBA tools use it), making keywords visually distinct from table and column names. lowercase is increasingly popular in modern teams, especially those who use ORMs. Preserve keeps whatever casing you typed. Many teams that use dbt write lowercase keywords, following the dbt style guide. The most important thing is consistency within your codebase.

Trailing commas (the default) put commas at the end of each line: col1, / col2, / col3. Leading commas put them at the start: col1 / , col2 / , col3. Leading commas make it easier to comment out a column without breaking the syntax (you never leave a trailing comma on the last line). They are common in some database teams, particularly those working with older SQL Server or Oracle codebases. Trailing commas are more standard and what most SQL formatters default to. Both are valid SQL.

SQL injection is an attack where malicious input manipulates your SQL query. Example: if you build SELECT * FROM users WHERE name = '${input}' and the user enters '; DROP TABLE users; --, the database may run a second statement that drops the table (on drivers that allow several statements per call). Formatting helps by making query structure visible: poorly structured queries are easier to spot when formatted. The real prevention is parameterized queries (prepared statements): SELECT * FROM users WHERE name = ? with the value passed separately. Never concatenate user input into SQL strings. Use parameterized queries in every language: cursor.execute("SELECT * FROM users WHERE name = %s", (name,)) in Python, db.prepare("SELECT...") in Node.js.

SQL databases (MySQL, PostgreSQL, SQLite, SQL Server, Oracle) store data in structured tables with rows and columns, enforce schemas, support ACID transactions, and use the Structured Query Language. They excel at complex queries with joins across multiple tables. NoSQL databases (MongoDB, Redis, Cassandra, DynamoDB) store data as documents, key-value pairs, graphs, or wide columns. They sacrifice some query flexibility for horizontal scalability and schema flexibility. When to use SQL: structured data with clear relationships, need for complex queries, transactional integrity required (banking, e-commerce). When to use NoSQL: unstructured or rapidly changing data, massive scale, simple access patterns. Many modern applications use both.

Window functions perform calculations across a set of rows related to the current row, without collapsing them into a group (unlike GROUP BY). Syntax: function() OVER (PARTITION BY col ORDER BY col ROWS BETWEEN ...). Common window functions: ROW_NUMBER(), RANK(), DENSE_RANK(), LAG(), LEAD(), SUM() OVER(), AVG() OVER(). They are supported in PostgreSQL, SQL Server, Oracle, MySQL 8+, and SQLite 3.25+. This formatter keeps each OVER (PARTITION BY ... ORDER BY ...) on one line so a window definition reads as one unit. Use the Highlight view to see the structure clearly.

WHERE filters rows before grouping: it operates on individual row values. HAVING filters groups after GROUP BY, it operates on aggregate values. Example: SELECT department, COUNT(*) as count FROM employees WHERE salary > 50000 GROUP BY department HAVING COUNT(*) > 5. Here WHERE removes individual employees with salary under 50K, then GROUP BY groups the remaining employees by department, and HAVING keeps only departments with more than 5 qualifying employees. You cannot use aggregate functions in WHERE; you must use HAVING for that. WHERE executes first and is more efficient as it reduces the data before grouping.

INNER JOIN: returns only rows where the join condition matches in both tables. Most common join type. LEFT JOIN: returns all rows from the left table, with matched rows from the right; unmatched right rows are NULL. Use when the left table is the "primary" entity. RIGHT JOIN: opposite of LEFT JOIN; rarely used (you can rewrite as LEFT JOIN). FULL OUTER JOIN: returns all rows from both tables; unmatched rows are NULL on the missing side. CROSS JOIN: returns every combination (cartesian product) of both tables; use with care on large tables. SELF JOIN: joins a table to itself, useful for hierarchies and adjacency lists. Most queries use INNER JOIN and LEFT JOIN. RIGHT JOIN and FULL OUTER JOIN are less common.

Yes, completely. All formatting happens in your browser using JavaScript. No SQL is sent to any server. This tool works offline once the page is loaded. We have no logs of what you paste. This makes it safe to paste queries containing sensitive column names, internal table structures, business logic, or even queries with embedded filter values. The only network request this page makes after load is for Google AdSense, which has no access to the content of the textarea. For extra caution, you can disconnect from the internet after the page loads and the tool will continue to work normally.

A CTE is a named temporary result set defined with WITH that you can reference in the main query. Syntax: WITH cte_name AS (SELECT ...) SELECT * FROM cte_name. CTEs improve readability by breaking complex queries into named, logical steps. Recursive CTEs (WITH RECURSIVE) can traverse hierarchical data like organizational charts or bill-of-materials. Multiple CTEs are separated by commas: WITH cte1 AS (...), cte2 AS (...) SELECT .... The formatter indents each CTE body inside its parentheses and starts the main query at the left margin below it. CTEs are supported in PostgreSQL, SQL Server, Oracle, MySQL 8+, and SQLite 3.8.3+. They are not the same as views: CTEs exist only for the duration of the query.

No. Databases ignore whitespace between tokens and treat keywords case insensitively, so a formatted query runs exactly like the original and uses the same execution plan. The only places where spaces and case matter are inside string literals and quoted identifiers, and this formatter never changes those.

Because WHERE is evaluated before SELECT, the alias does not exist yet. SELECT price * qty AS total FROM items WHERE total > 100 fails in standard SQL and PostgreSQL. Repeat the expression (WHERE price * qty > 100) or wrap the query in a subquery or CTE. ORDER BY runs after SELECT, so aliases work there.