SQL Formatter
SQL Formatter re-indents a query in your browser. It tokenises the SQL first rather than matching keywords with a regular expression, which is the difference between a formatter that works and one that breaks on the first string literal containing the word FROM.
How does SQL Formatter work?
A SQL formatter is judged on what it does to the query it was not expecting. Four things separate one that is safe to run over a codebase from one that quietly corrupts a migration.
Tokenising, not pattern matching
The obvious implementation finds keywords with a regular expression and inserts line breaks. It works until a query contains 'FROM cache' as a string literal, or a comment containing an apostrophe, and then it produces something that no longer parses. Here the query is tokenised first — strings, identifiers, comments, numbers, operators and keywords are separate things — and only keyword tokens affect layout. The sample query above contains a string literal with the word FROM in it for exactly this reason.
Quoted identifiers are never rewritten
"User" and User are different objects on a case-sensitive database. A formatter that “tidies” the quotes away has changed which table the query reads. The quoting style also differs by dialect — MySQL's backtick, T-SQL's square brackets, the standard double quote — so the dialect selector is about parsing correctness rather than output style.
Dialect-specific string rules
The standard escape for a quote inside a string is doubling it; MySQL also honours a backslash. Postgres has dollar-quoted strings whose delimiter is chosen by the author — $func$ … $func$ — which exists precisely so a function body can contain anything. A tokeniser that does not know these will end a string early and everything after it is misclassified.
Comments keep their position
A comment on its own line stays on its own line; a trailing comment stays trailing. Block comments nest in standard SQL, unlike C, so /* a /* b */ c */ is one comment and not a syntax error. Minifying converts a -- comment to a block comment, because on one line a line comment would comment out the rest of the query — a small detail that turns a minified query into a broken one.
The layout the output uses
Clauses start lines; joins start lines; columns get one line each; boolean connectives in a WHERE start a continuation line, but only at the top level — an AND inside (y = 2 AND z = 3) is part of an expression and stays where it is. Leading commas are available because they make a column list easier to reorder in a diff, which is a real argument even if it looks strange at first.
Input
select a,b from t where x=1 and y=2
Formatted
SELECT a, b FROM t WHERE x = 1 AND y = 2
What options and edge cases does SQL Formatter support?
| Parameter | Type | Default | Behaviour & edge cases |
|---|---|---|---|
| Dialect | standard / postgres / mysql / tsql | postgres | Affects parsing, not style: which characters quote an identifier, how a string escapes a quote, and whether dollar-quoting and # comments exist. |
| Uppercase keywords | boolean | on | Keywords only. Identifiers, strings and comments keep their original case, because changing an identifier's case changes which object it names on some databases. |
| Column per line | boolean | on | One selected column per line. Makes a diff show which column changed rather than that the SELECT line changed. |
| Leading commas | boolean | off | Comma before the column rather than after. Divisive, and genuinely easier to reorder: adding a column at the end does not touch the previous line. |
| String literals | preserved | byte for byte | Including whitespace inside them. A string is data, and reformatting data is not formatting. |
| Quoted identifiers | preserved | — | "User" is not User on a case-sensitive database. The quoting is never added or removed. |
| Block comments | nested | — | Standard SQL nests them, unlike C. /* a /* b */ c */ is one comment, and a tokeniser that stops at the first */ misreads the rest of the query. |
| Minify | one line | — | Collapses whitespace and converts line comments to block comments, because a -- comment on one line comments out everything after it. |
Frequently asked questions
Why does the sample query have 'FROM cache' in it?
To demonstrate the difference between a tokenising formatter and a regex one. A formatter that finds keywords by pattern sees the FROM inside that string literal, inserts a line break in the middle of it, and produces a query that no longer parses. Tokenising first means a string is a string whatever it contains.
Will it change my table or column names?
No. Only keyword tokens are recased, and quoted identifiers are passed through byte for byte, including their quotes. This matters more than it sounds: on Postgres, "User" and User name different tables, so a formatter that removes quotes changes the meaning of the query rather than its appearance.
Which dialect should I pick?
The one your database actually is, because the setting is about parsing rather than style. MySQL uses backticks for identifiers and honours backslash escapes in strings; T-SQL uses square brackets; Postgres has dollar-quoted strings. Picking the wrong one means a string or identifier ends in the wrong place and everything after it is misclassified.
Leading commas — why would anyone?
Because adding or removing the last column in a list touches one line instead of two. With trailing commas, appending a column means editing the previous line to add a comma, so the diff shows two changed lines and a reviewer has to check both. It looks strange for about a day and then stops mattering.
Does minifying a query make it faster?
No. The database parses the query before planning it, and whitespace is discarded at that stage; a minified query and a formatted one produce identical plans. Minifying is for getting a query onto one line — into a log, a config file, a shell command — not for performance.
Is my query uploaded?
No. Tokenising and formatting run in the tab you already have open. It matters here because queries carry things people would not paste anywhere: table and column names that describe a business, and often literal values that are real customer data.