SQL Formatter
Format, beautify, and minify SQL queries instantly. Supports SELECT, INSERT, UPDATE, DELETE, and more. Free, browser-based.
- Runs in your browser
- Your data never leaves your browser
- Free · No Sign-Up
Scan with WeChat to share this tool
Examples, details and FAQ Worked examples, how it compares with other tools, and answers to common questions.
Worked Examples
Before formatting
select u.id, u.name, o.total from users u join orders o on u.id = o.user_id where o.total > 100 and u.active = 1 order by o.total desc limit 10;
After formatting
SELECT
u.id,
u.name,
o.total
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.total > 100
AND u.active = 1
ORDER BY o.total DESC
LIMIT 10;
Aggregates and Comments
GROUP BY, HAVING and ORDER BY each start a new line at the same level as FROM. Comments stay where they were, at the end of the line they belonged to:
select status, count(*) as n -- per status
from orders /* this year */ where created_at >= '2026-01-01' group by status having count(*) > 5 order by n desc;
SELECT
status,
COUNT(*) AS n -- per status
FROM orders /* this year */
WHERE created_at >= '2026-01-01'
GROUP BY status
HAVING COUNT(*) > 5
ORDER BY n DESC;
Several statements separated by ; are formatted one after another, each starting on a new line.
Minify
Minify puts the whole query on one line, for example to paste it into a log search, a shell variable or a JSON string. -- and /* */ comments are dropped, because a -- comment would swallow everything after it on a single line. A # comment or a --x without a space is kept and the line ends after it, because other databases read those characters as code (see the FAQ on dialects). MySQL executable comments /*! … */ and optimizer hints /*+ … */ are code too, so both outputs keep them as written. Minifying the formatted aggregate query above gives:
SELECT status, COUNT(*)AS n FROM orders WHERE created_at >= '2026-01-01' GROUP BY status HAVING COUNT(*)> 5 ORDER BY n DESC;
The missing space in COUNT(*)AS looks odd but does not change the query; SQLite 3 runs this line as written. Format adds the space back.
Write Statements
INSERT, UPDATE and DELETE are handled, but with less layout than SELECT:
UPDATE orders
SET status = 'shipped', shipped_at = now()
WHERE id = 42;
INSERT INTO users(name, email)
VALUES('Ann', 'ann@example.com'),('Bo', 'bo@example.com');
The column list after SET stays on one line, and all VALUES rows go on one line, with the space before each ( removed. For a long multi-row insert, add the line breaks by hand.
Keyword Case
With the uppercase option on, the formatter capitalizes a fixed list of 54 words: clause keywords such as SELECT, LEFT, HAVING, UNION, CASE, EXISTS, plus the five aggregate functions COUNT, SUM, AVG, MIN and MAX. Other function names (now(), coalesce(), date_trunc()) and data types are left as typed. With the option off, the same keywords are converted to lower case, so Select Id From Users becomes select Id from Users. Unquoted names, including names with accented or non-Latin letters such as café or 社員番号, are never changed, and quoted names ("Order Id", `name`) and string literals, including doubled quotes as in 'it''s here', are copied as written.
Names with other letters are kept whole as well. select café, naïve from t minifies to SELECT café, naïve FROM t.
Subquery Example
Output with the default options (2-space indent, uppercase keywords). A subquery in parentheses is indented one level deeper:
select * from users where id in (select user_id from orders where total > 100);
SELECT
*
FROM users
WHERE id IN (
SELECT
user_id
FROM orders
WHERE total > 100
);
CTE Example
The body of a WITH ... AS (...) block is indented like a subquery. Function calls such as SUM(total) stay on one line, and so does a window function such as ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...):
with recent as (select user_id, sum(total) as spent from orders group by user_id) select u.name, r.spent from users u join recent r on r.user_id = u.id order by r.spent desc;
WITH recent AS (
SELECT
user_id,
SUM(total) AS spent
FROM orders
GROUP BY user_id
)
SELECT
u.name,
r.spent
FROM users u
JOIN recent r ON r.user_id = u.id
ORDER BY r.spent DESC;
Limits
- One generic dialect, no validation. There is no MySQL, PostgreSQL, or SQL Server mode, and invalid SQL is formatted without an error.
- Backslashes in strings follow one of two readings. MySQL reads
\'inside a string as an escaped quote unless theNO_BACKSLASH_ESCAPESSQL mode is on (MySQL 8.4 String Literals); PostgreSQL with its defaultstandard_conforming_strings = on(PostgreSQL: Lexical Structure) and SQLite (SQLite: Literal Values) treat a backslash as an ordinary character. The tool uses the MySQL reading when it closes every quoted string, and the standard reading otherwise. Soselect * from users where note = 'It\'s'staysSELECT * FROM users WHERE note = 'It\'s', and a lone'C:\'stays one string. Strings are always copied as written; the reading only decides where each string ends. - A
#or--xsegment is copied to the end of its line. The rest of that line is not formatted, so after a PostgreSQL#operator or a SQL Server#tempname the keywords keep their case and stay on the line. If such a segment opens a string or a/* */comment that continues on the next line (valid in MySQL), the result can differ from the input. CASEstays on one line, except forAND/OR.CASE WHEN ... THEN ... ENDis not broken intoWHENlines, but anANDorORinside it starts a new line as inWHERE:select case when a = 1 and b = 2 then 'x' else 'y' end as k from tgives aCASE WHEN a = 1line followed byAND b = 2 THEN 'x' ELSE 'y' END AS k.
SELECT
CASE WHEN a = 1
AND b = 2 THEN 'x' ELSE 'y' END AS k
FROM t
- Write statements are only lightly formatted. See the
INSERTandUPDATEexamples above;CREATE TABLEcolumn definitions also stay on one line. - Minify removes
--and/* */comments (/*! … */and/*+ … */are kept; a#comment or--xwithout a space is kept and ends the line) and the space after a closing parenthesis, for exampleSELECT COUNT(*)FROM events.
FAQ
What SQL dialects are supported?
There is one generic mode for SELECT, INSERT, UPDATE, DELETE, CREATE TABLE, JOINs, subqueries and CTEs, with no MySQL, PostgreSQL, SQLite or SQL Server switch. Common dialect tokens are copied unchanged: backtick and [bracketed] names, N'...', X'...' and PostgreSQL E'...' strings, $$...$$ dollar-quoted strings, and parameters such as $1, ?1, :id, @id and @@session.sql_mode. Backslashes in strings are read the MySQL way only when that reading closes every string. Minify drops a comment that starts with -- and a space and plain /* */ comments, keeps MySQL /*! */ executable comments and /*+ */ optimizer hints; a # comment (MySQL) or --x without a space, which other databases read differently, is kept to the end of its line and the line ends there.
Does it validate my SQL?
No. This tool focuses on formatting, not validation. It beautifies the structure of your query but does not check for syntax errors or schema correctness, and invalid SQL is formatted without an error.
Can it format long queries with joins, subqueries and CTEs?
Yes. Each JOIN starts a line, a subquery or the body of WITH ... AS (...) is indented one level deeper, and function calls such as SUM(total) or ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...) stay on one line. CASE expressions stay on one line except that AND / OR inside them start a new line, and the column list of CREATE TABLE stays on one line.
Can I customize the formatting?
Yes. Choose 2-space, 4-space or tab indentation, and turn uppercase keywords on or off. Typing formats with the current options; after you change an option, click Format to apply it to the current output.
Do names with accents or Chinese, Japanese or Korean characters need quotes?
Not for this tool. An unquoted name is read as any run of Unicode letters, digits, combining marks, _ and $, so café, 社員番号 or 가입일 stays one name and keeps its spelling; only the 54 ASCII keywords change case. Whether your database accepts such a name without quotes is up to the database: MySQL allows U+0080 to U+FFFF in unquoted names, and PostgreSQL allows non-Latin letters.
Is my data sent anywhere?
No. The query is formatted in this browser tab and is not sent to a server or saved in browser storage. When you click Format or Minify, the page statistics record the tool name and that action, not the SQL.