FIRST CH TOOLS / 29 SQL Formatter
SQL Query Formatter & Beautifier
Paste the kind of one-line SQL you pull out of a log or a code file on the left, and a query with uppercase keywords, one clause per line and real indentation comes out on the right. JOIN … ON, subqueries and CASE expressions each land at their own depth. Indent width, comma position and where AND goes are yours to choose.
Paste and it formats as you type
Nothing you paste leaves the browser — the lexer and the formatter both run on this page. Loading a file only reads it from your device; nothing is uploaded. You can call the tool straight from a URL: /en/sql-format/?sql=select%20*%20from%20t%20where%20id%3D1 or /en/sql-format/?sql=…&case=lower&indent=2&comma=leading
Where SQL gets hard to read
Formatting never changes the meaning
SELECT a FROM t WHERE b = 1
↓ the database receives exactly the same query
SELECT
a
FROM t
WHERE b = 1
To SQL, whitespace and newlines are only separators. Formatting is for humans: the execution plan and the result set stay the same. That also means reformatting a hard-to-read query during review is always safe. Whitespace inside a string literal does matter, so this tool never touches what is between '…'.
Uppercase keywords are about reading speed
select id, name from users where deleted_at is null SELECT id, name FROM users WHERE deleted_at IS NULL
SQL keywords work in any case. Uppercase is common because it separates keywords from column and table names at a glance: if your identifiers are lowercase, the uppercase words alone show you the skeleton of the query (SELECT / FROM / WHERE / GROUP BY). Plenty of teams prefer all-lowercase instead, so this tool offers uppercase, lowercase and leave-as-typed.
Identifier case means different things per database
SELECT "Name" FROM users; -- PostgreSQL: the Name column (case preserved) SELECT Name FROM users; -- PostgreSQL: folded to the name column
Unlike keywords, identifier folding is database-specific. PostgreSQL folds unquoted identifiers to lowercase, Oracle to uppercase, and MySQL table names may or may not be case-sensitive depending on the file system. To avoid breaking anything, this tool never changes the spelling of an identifier, and leaves whatever sits inside "…", `…` or […] exactly as it is.
Implicit joins (tables separated by commas)
SELECT * FROM orders o, users u WHERE u.id = o.user_id -- the old way SELECT * FROM orders o INNER JOIN users u ON u.id = o.user_id
With commas, the join condition and the filter live in the same WHERE clause, so forgetting one condition silently turns the query into a cross join and the row count explodes. An explicit JOIN … ON keeps the join in ON and the filter in WHERE. Outer joins can only be written with explicit joins — the (+) and *= dialects are deprecated or gone. This tool flags comma-separated FROM clauses.
UPDATE and DELETE without a WHERE
DELETE FROM logs; -- every row is gone UPDATE users SET flag = 1; -- every row is rewritten
This is the scariest thing hiding in a one-line query. A missing WHERE is still valid SQL, so it runs happily. This tool raises a warning whenever it sees an UPDATE or DELETE with no WHERE clause. Before running one for real, count the target rows with a SELECT that uses the same condition.
Keep SELECT * out of application code
SELECT * FROM users; -- add a column, change the result SELECT id, name, email FROM users; -- pin down what you need
It is handy for ad-hoc digging, but in application code it means the payload and the shape of the result change whenever a column is added. A query that was served entirely from a covering index may start hitting the table itself. Listing the columns also tells a reviewer what the query actually needs.
Pass values as placeholders, never string concatenation
-- dangerous (SQL injection) "SELECT * FROM users WHERE name = '" + input + "'" -- safe SELECT * FROM users WHERE name = ? -- :name / $1 work the same way
What stops SQL injection is not escaping but placeholders (prepared statements): the value never gets a chance to be parsed as syntax. This tool recognises ?, :name, $1 and @var and leaves them alone. When you format a query copied out of a log, put the values back into placeholders instead of reusing the inlined form.
Indentation is how subquery depth becomes visible
WHERE o.id IN (
SELECT order_id
FROM payments
WHERE captured_at IS NOT NULL
)
A subquery is unreadable unless you can see where the inner query starts and ends. This tool treats a parenthesis as a subquery only when SELECT, WITH or VALUES follows it, and keeps function calls on one line (SUM(…), IN (1, 2, 3)). Once you are three levels deep, pulling the inner queries out into a WITH clause (CTE) flattens things out.
Leading commas vs trailing commas
SELECT SELECT
a, a
b, , b
c , c
Leading commas keep the diff small: removing the last column does not touch the line above it. Trailing commas read in the natural order and are the default in most style guides. Neither is wrong — being consistent inside the team matters far more. This tool writes either one.
AND at the start of a line makes conditions countable
WHERE o.status = 'paid'
AND u.deleted_at IS NULL
AND o.total >= 1000
With AND at the start of each line, the number of ANDs is the number of conditions, and adding or removing one is a one-line diff. When OR is mixed in, make the precedence (AND binds tighter than OR) explicit with parentheses. The AND in BETWEEN a AND b is not a condition separator, so this tool never breaks there.
Comments are kept
SELECT id -- primary key FROM t /* what we aggregate */
Both -- / # line comments and /* … */ block comments survive formatting. A line comment runs to the end of its line, so the formatter never appends anything after one (it would be swallowed). An unclosed block comment turns everything after it into a comment, so that gets flagged.
Dialects pass through untouched
`mysql_style` / "postgres_style" / [sqlserver_style] ::text (cast) ->> (JSON) E'…' (escape string) $$…$$
This is a lexical formatter, not a parser for one specific database. MySQL backticks, SQL Server brackets, PostgreSQL casts, JSON operators and dollar quoting all survive as written. The flip side is that it does not report syntax errors — only unbalanced parentheses and quotes are counted and flagged.
Supported input: SELECT / INSERT / UPDATE / DELETE / WITH (common table expressions), set operations such as UNION, every flavour of JOIN, CASE expressions, window functions (OVER (PARTITION BY … ORDER BY …)) and multiple statements separated by ;. Strings, quoted identifiers, comments and placeholders are preserved verbatim.
How to Use
- Paste the queryDrop the one-line query you pulled out of a log or a code file into the left box. You can also drag and drop a .sql file.
- Pick your styleKeyword case, indent width (2/4/8 spaces or a tab), comma position and where AND goes. Tick Collapse onto one line to go the other way.
- Copy the resultThe formatted query appears on the right. The preview below colour-codes keywords, strings and comments, and anything risky shows up in the findings list.
About This Tool
It turns one-line SQL into clauses on their own lines. Queries from application logs, from an ORM, or pasted into a chat almost always arrive with the newlines squashed out. This tool splits the query into tokens (keywords, identifiers, strings, operators, comments) and rebuilds it with SELECT, FROM, WHERE, GROUP BY and ORDER BY at the start of a line and their contents indented one level. The meaning does not change — SQL only sees whitespace as a separator.
Keywords are cased; identifiers are not. Keywords and type names are matched against a keyword table and raised to uppercase (or lowered), but table, column and alias spellings are left exactly as typed. Identifier folding differs per database (PostgreSQL folds to lowercase, Oracle to uppercase) and quoted identifiers are case-sensitive, so changing them would be a real risk. Anything inside "…", `…`, […] or '…' is passed through byte for byte.
Parentheses are treated by what is inside them. Only a ( followed by SELECT, WITH or VALUES becomes an indented subquery; everything else (SUM(…), COALESCE(…), IN (1, 2, 3), OVER (PARTITION BY …)) stays on one line, because stretching argument lists vertically makes them harder to read, not easier. CASE expressions are laid out with WHEN, ELSE and END stacked, and the AND in BETWEEN a AND b never becomes a line break.
It flags the dangerous shapes while it formats. An UPDATE or DELETE with no WHERE (every row is affected), unbalanced parentheses or quotes, SELECT *, tables comma-joined in the FROM clause, and whether placeholders are used all show up under “What we found”. These are exactly the things you miss while squinting at a single long line.
It bends to your team's style. Indent with 2, 4 or 8 spaces or a tab; put commas at the end or the start of a line; put AND / OR first or last. Break after every clause gives a fully expanded layout where even FROM and WHERE put their contents on the next line, and Collapse onto one line folds everything back into a single line for pasting elsewhere. Every setting has a URL parameter, so an internal doc can link to the exact style you use.
To review what changed, pair this with the Diff Checker; to paste a schema or a result set into a document, use the Markdown Table Generator; to check when a batch job actually fires, use the Cron Explainer.
From AI Agents
This formatting logic is also available as the sql_format tool of the MCP (Model Context Protocol) server @first-ch/tools-mcp, so an AI agent can call it directly without a browser. It can read and write files by path too. See Using these tools from AI agents for setup details.
Setup
claude mcp add firstch-tools -- npx -y @first-ch/tools-mcp
Examples
# Format a one-line query (uppercase keywords, one clause per line) sql_format(text="select a,b from t where a=1 and b=2") # Read a .sql file, format it and write it back sql_format(path="/tmp/report.sql", outputPath="/tmp/report.sql") # Match a house style (2 spaces, leading commas, lowercase keywords) sql_format(text="select a, b from t", indent="2", commaStyle="leading", keywordCase="lower") # Collapse a formatted query back onto one line sql_format(text="SELECT\n a\nFROM t", compact=true)
Other Tools
- 01Batch Image → WebPWebP Converter
- 02White Background RemoverWhite BG Remover
- 03WCAG Contrast CheckerContrast Checker
- 04Character CounterCharacter Counter
- 05llms.txt Generatorllms.txt Generator
- 06JSON-LD GeneratorJSON-LD Generator
- 07Markdown → PDFMD → PDF
- 08OGP Meta Tag WizardOGP Wizard
- 09Favicon GeneratorFavicon Generator
- 10TikTok PublisherTikTok Publisher
- 11Encoding & Line Ending ConverterEncoding Converter
- 12Batch Image → AVIF + pictureAVIF Converter
- 13Test Data GeneratorTest Data Generator
- 14Marp Markdown → SlidesMarp Slides
- 15Text & Code Diff CheckerDiff Checker
- 16Cron Explainer & Next RunsCron Explainer
- 17Base64 & Data URI EncoderBase64 & Data URI
- 18URL Parameter Editor & UTM BuilderURL Parameters
- 19HTML Entity Escape & UnescapeHTML Escape
- 20JSON ⇄ YAML ConverterJSON ⇄ YAML
- 21PX ⇄ REM / EM ConverterPX ⇄ REM / EM 単位変換
- 22Color Converter & AlphaColorコード変換&アルファ透過
- 23MD5 / SHA-256 Hash Generatorハッシュ生成
- 24JWT Decoder & Expiry CheckerJWTデコーダー&有効期限チェッカー
- 25User-Agent ParserUser-Agent解析&デバイス判定
- 26UUID & ULID GeneratorUUID (v4) & ULID 一括生成
- 27Aspect Ratio Calculatorアスペクト比計算&サイズ算出
- 28Markdown Table GeneratorMarkdownテーブル整形&CSV/TSV変換
- 30QR Code GeneratorQR Code Generator
- 31Regex Tester正規表現テスター
- 32Unix Timestamp ConverterUNIXタイムスタンプ⇄日時変換
- 33New Tab Memo — Chrome Extension新規タブメモ帳 Chrome拡張
- 34Image Resizer & Cropper画像リサイズ&クロップ
- 35EXIF Viewer & RemoverEXIF情報の確認&除去
- 36robots.txt Generatorrobots.txt ジェネレーター
- 37Password Generator安全なパスワード生成
- 38Case Converter文字列ケース変換