SQL Query Formatter

Beautify SQL with configurable indentation, keyword casing and dialect support for PostgreSQL, MySQL, SQL Server, SQLite and Oracle.

Paste a query — or a whole script of them — and the formatter rebuilds it with a consistent style: major clauses at column zero, joins aligned with FROM, AND/OR and subqueries indented, long column lists split one item per line. Pick the dialect you are targeting and the formatter applies that engine's conventions — TOP vs LIMIT, :: casts, GO batch separators, N' wide strings, # comments — while minify mode squeezes the script back to a single line.

🔒 100% private — nothing is uploaded or stored. Your SQL is tokenized and formatted entirely in your browser with plain JavaScript. Queries, connection strings and anything else you paste never touch our servers, are never written to disk, and the page keeps working even if your internet connection drops.

Your SQL

Tip: press Ctrl+Enter (or ⌘+Enter) inside the box to format.

Dialect
Keywords
Indent
Wrap lines at
Recognised by the PostgreSQL profile:
    Paste or type SQL above — formatting runs as you type.

    How the formatter indents your queries

    Every rule below is applied as you type, so the output is stable and diff-friendly:

    • Major clauses start at column zero. SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, UNION and their friends each begin a new line at the outermost level.
    • Joins align with FROM. LEFT JOIN, CROSS JOIN and the rest sit at the same indent as FROM; their ON/USING conditions step in one level.
    • Conditions step in. AND/OR lines are indented one level under the clause they belong to, so a ten-condition WHERE reads as a tree.
    • Subqueries and CTE bodies are indented. Anything inside a (SELECT …) or a WITH body gets one extra level, and the closing parenthesis returns to the outer level.
    • CASE arms are indented. WHEN/THEN/ELSE sit one level in; END aligns with CASE.
    • Long lists split one item per line. When a line passes your wrap width, column lists, function arguments and VALUES rows break after each comma.
    • Statements are separated by blank lines, and comments are preserved where you wrote them.
    • Keyword casing touches keywords only. Identifiers, table names and string contents keep their original casing.

    Dialect differences that matter

    The same logical query is spelled differently on each engine. The formatter's dialect profile knows these conventions, so it never mangles a TOP (10), splits a GO batch in half, or treats a # comment as an operator:

    FeaturePostgreSQLMySQLSQL ServerSQLiteOracle
    Quoted identifier"col"`col`[col]"col" or `col`"col"
    Top-N rowsLIMIT nLIMIT nTOP (n)LIMIT nFETCH FIRST n ROWS ONLY / ROWNUM
    Pagination offsetLIMIT n OFFSET mLIMIT m, nOFFSET (m) ROWS FETCH NEXT n ROWS ONLYLIMIT n OFFSET mOFFSET m ROWS FETCH NEXT n ROWS ONLY
    Boolean literalsTRUE / FALSE1 / 01 / 01 / 01 / 0
    Line comments---- and #---- and #--
    Parameter placeholders$1, ?, :name?@name?, :name, @name:name
    Type castx::int or CASTCASTCAST / CONVERTCASTCAST / TO_NUMBER
    String concatenationa || bCONCAT(a, b)a + b or CONCATa || ba || b
    Empty string vs NULLdistinctdistinctdistinctdistinct'' is NULL

    What the formatter does — and does not

    This is a formatter, not a linter: it reflows what you give it and never rewrites logic. It will not add a missing GROUP BY column, convert LIMIT to TOP for you, or tell you a join is missing an ON clause. The dialect profile only changes how the formatter recognises engine-specific syntax (quoted identifiers, comments, casts, batch separators, placeholders) so that none of it gets mangled. If you need to convert a query between dialects, do that pass by hand — the formatted output is the cleanest place to start.

    Nothing on this page leaves your browser: there is no server round-trip when you paste, format or minify, no analytics on your inputs, and no storage of any kind.


    Luke Keam
    Thank you for reading. Get in touch
    Luke Keam | techgeek.biz