In short: what does this SQL tool do?
A SQL formatter re-indents raw or minified SQL into clean, readable queries. This toolkit goes far beyond formatting: it validates syntax, lints against 25+ rules (security, performance, naming, anti-patterns), computes a 0–100 health score, checks compatibility across 9 databases, analyzes execution plans, generates documentation, and ships a template library — 100% in your browser, with no query ever uploaded.
Format & validate
Beautify across 10 dialects with custom case and indentation; catch errors with line numbers.
Lint with auto-fix
25+ rules across security, performance, naming and anti-patterns — with one-click fixes.
0–100 health score
Security, performance, maintainability, readability and complexity, weighted into one figure.
9-database compatibility
See which dialect-specific syntax ports to MySQL, Postgres, SQL Server, Oracle and more.
Execution-plan analyzer
Paste EXPLAIN output to find scans, costly joins and missing indexes.
100% private
All parsing, linting and scoring run in your browser — your SQL is never uploaded.
What is a SQL formatter and analyzer?
SQL (Structured Query Language) is the standard language for defining, querying and manipulating relational data. Because SQL is whitespace-insensitive, the same query can be written as one cramped line or as a neatly indented block — and teams almost never agree on a single style by hand. A SQL formatter (also called a beautifier or pretty-printer) parses your query into tokens and re-prints it with consistent keyword casing, indentation and line breaks, so the clause structure — SELECT, FROM, JOIN, WHERE, GROUP BY — is obvious at a glance.
This tool is built around a hand-written SQL tokenizer that understands string literals, quoted identifiers ("col", `col`, [col]), comments, numbers, operators, bind parameters and dialect quirks. On top of that tokenizer sit a formatter, a minifier, a validator, a linting engine, a quality scorer, a compatibility checker, an execution-plan analyzer and a documentation generator. Everything runs client-side in JavaScript, so you can safely paste production queries, internal table names and sensitive logic — nothing is transmitted, logged or stored on a server.
SQL tutorial: the core statements
Almost all day-to-day SQL falls into a handful of statement types. Understanding their shape makes every query you read or write far clearer.
- SELECT — reads rows. The canonical order is
SELECTcolumns,FROMa table, optionalJOINs, aWHEREfilter,GROUP BYfor aggregation, aHAVINGfilter on groups,ORDER BYto sort andLIMITto cap rows. - INSERT — adds rows:
INSERT INTO t (a, b) VALUES (1, 2). Always list columns explicitly so a schema change never silently shifts your values. - UPDATE — modifies existing rows:
UPDATE t SET a = 1 WHERE id = 5. TheWHEREclause is mandatory in practice — without it, every row changes. - DELETE — removes rows:
DELETE FROM t WHERE id = 5. Same warning as UPDATE. - DDL (
CREATE,ALTER,DROP,TRUNCATE) — defines and changes the schema itself: tables, columns, indexes, views, procedures and triggers.
A logical detail that trips up beginners: SQL does not execute in written order. The database evaluates FROM and JOIN first, then WHERE, then GROUP BY and HAVING, then SELECT(which is why a column alias defined in SELECT usually can't be used in WHERE), then ORDER BY, and finally LIMIT. Keeping that evaluation order in mind explains many "column does not exist" errors and helps you reason about performance.
Joins combine rows from multiple tables. An INNER JOIN keeps only matching pairs; a LEFT JOIN keeps every left-hand row and fills unmatched right-hand columns with NULL(perfect for "find rows with no match" via WHERE right.id IS NULL); FULL JOIN keeps both sides. Prefer explicit JOIN … ON over comma joins — the latter hide the join condition in the WHERE clause and make accidental cross joins easy. The linter in this tool flags comma joins for you.
SQL performance guide
Most slow queries share a small set of root causes. Learning to spot them — and letting the linter and execution- plan analyzer in this tool point them out — will resolve the majority of database performance problems.
- Full table scans. When the database reads every row because no usable index exists for your filter, latency grows linearly with table size. In a plan this appears as
Seq Scan(PostgreSQL) ortype: ALL(MySQL). Fix it by indexing the filtered/joined columns. - Non-sargable predicates. Wrapping an indexed column in a function —
WHERE YEAR(created_at) = 2024— disables the index. Rewrite as a range:created_at >= '2024-01-01' AND created_at < '2025-01-01'. - SELECT *. Fetching every column inflates I/O and network transfer and prevents covering-index optimizations. List only the columns you need.
- Leading-wildcard LIKE.
LIKE '%term'can't use a normal B-tree index; use a trailing wildcard, full-text search, or a trigram index. - N+1 queries. Running one query per row in application code instead of a single set-based join is the most common ORM performance bug. Batch with a join or
INlist. - Implicit type casts. Comparing a string column to a number (or vice-versa) can force a cast that defeats the index. Match types exactly.
The golden rule of SQL tuning is to measure, don't guess. Capture an EXPLAIN ANALYZE for the real query and paste it into the Execution Plan tab here: it will identify the costliest operations, flag sequential scans and nested loops over large inputs, and recommend specific indexes.
Database indexing guide
An indexis an auxiliary data structure — almost always a B-tree — that lets the database find rows by a column's value without scanning the whole table, much like the index at the back of a book. Indexes dramatically speed up reads but add cost to writes (every INSERT/UPDATE/DELETE must also maintain them) and consume storage, so they are a deliberate trade-off rather than a free win.
- Single-column indexes accelerate equality and range filters on one column.
- Composite indexes cover multiple columns and follow the left-prefix rule: an index on
(a, b, c)helps queries filtering ona, ona, b, or ona, b, c— but not one filtering onbalone. Order the columns most-selective-first, and put equality columns before range columns. - Covering indexesinclude every column a query needs, so the database answers it from the index alone (an "index-only scan") without touching the table.
- Unique indexes enforce uniqueness and double as a fast lookup. Primary keys are backed by one automatically.
- Partial / filtered indexes index only rows matching a condition (e.g.
WHERE deleted_at IS NULL), keeping the index small and hot. - Functional / expression indexes index the result of an expression, restoring index use for otherwise non-sargable predicates like
LOWER(email).
Avoid over-indexing: redundant indexes slow writes and confuse the planner. Build large indexes online where possible (PostgreSQL CREATE INDEX CONCURRENTLY) to avoid locking writes, and periodically review unused indexes. The execution-plan analyzer here recommends missing indexes based on the scans it detects.
Query optimization guide
Beyond indexing, the structure of a query itself matters. These rewrites consistently pay off:
- Filter early and narrow. Apply the most selective
WHEREconditions first and avoid pulling columns or rows you'll discard later. - Prefer EXISTS / JOIN over IN (subquery) for correlated checks, and prefer
NOT EXISTSor an anti-join overNOT IN, which returns no rows if the set contains aNULL. - Replace deep nesting with CTEs.
WITHclauses turn a tangle of subqueries into named, sequential steps that read top-to-bottom and are easier to optimize and debug. The complexity score in this tool rewards flattening nesting with CTEs. - Use window functions (
ROW_NUMBER(),RANK(),SUM() OVER (…)) for running totals, top-N-per-group and period comparisons instead of self-joins. - Paginate with keyset pagination (
WHERE id > :last_id ORDER BY id LIMIT 20) instead of largeOFFSETvalues, which still scan and discard all skipped rows. - Keep statistics fresh. Run
ANALYZEso the planner has accurate row estimates; a bad estimate is the hidden cause behind many surprising plans.
Optimization is iterative: change one thing, re-run EXPLAIN ANALYZE, compare. Paste each plan into the Execution Plan tab to keep the feedback loop tight.
SQL security guide
SQL is a frequent attack surface and an easy place to cause irreversible damage. The linter in this tool surfaces the most important risks, but the underlying principles are worth internalizing.
- Always parameterize. Never build SQL by concatenating user input. Use bind parameters (
?,:name,$1) so the database treats input strictly as data — this is the single most effective defense against SQL injection. The linter flags concatenation that looks like hand-built SQL. - Guard destructive writes. A
DELETEorUPDATEwithout aWHEREclause touches every row. The linter raises this as an error. - Apply least privilege. Grant only the specific privileges a role needs; avoid
GRANT ALL. Application accounts rarely need DDL or superuser rights. - Protect against irreversible DDL.
DROPandTRUNCATEare unrecoverable without a backup — gate them behind reviewed migrations. - Minimize data exposure. Select only the columns you need, mask or omit PII in logs, and avoid returning secrets in error messages.
Database design guide
Good schema design prevents whole classes of bugs and performance problems. A few durable principles:
- Normalize first, denormalize deliberately. Aim for third normal form (3NF) — every non-key column depends on the key, the whole key, and nothing but the key — then denormalize only where measured read performance demands it.
- Choose keys carefully. Every table should have a primary key. Surrogate keys (auto-increment / UUID) decouple identity from changeable business data. Enforce real relationships with foreign keys.
- Pick the right types. Use the narrowest correct type —
DATE/TIMESTAMPfor time, exactDECIMALfor money (neverFLOAT), and constrainedVARCHAR(n)where appropriate. - Name consistently. Pick one convention —
snake_caseis the SQL norm — for tables and columns, and stick to it. The linter flags mixed identifier casing. - Add constraints.
NOT NULL,UNIQUE,CHECKand foreign keys move data-integrity rules into the database where they can't be bypassed.
Paste your CREATE TABLE statements into the Documentation tab to instantly generate a column/key/ relationship reference you can export to Markdown or HTML.
SQL linting and quality scoring
A SQL linter performs static analysis on the query text, flagging issues without executing anything. This tool runs rules across six categories — naming, formatting, anti-patterns, performance, security and maintainability— each with a severity (error, warning, info) and, where safe, a one-click fix. "Fix all" re-formats and applies every safe correction at once.
The quality score distils all of this into a single 0–100 health score, plus five sub-scores: Security, Performance, Maintainability, Readability and Complexity. The overall figure is a weighted blend (security and performance carry the most weight) and is fully deterministic — the same query always scores the same — so you can track improvement as you refactor, or wire the report into code review. Export the full report as Markdown, HTML, JSON or CSV from the Quality tab.
Cross-database compatibility
SQL is a standard, but every database extends it. A query written for MySQL often won't run on SQL Server or Oracle without changes. The Compatibility tab scans your query for dialect-specific features and shows, for nine databases, whether each is supported, partially supported or unsupported:
- Row limiting:
LIMIT(MySQL/Postgres/SQLite) vsTOP(SQL Server) vsROWNUM/FETCH FIRST(Oracle). - Auto-increment:
AUTO_INCREMENT(MySQL) vsSERIAL(Postgres) vsIDENTITY(SQL Server) vs sequences (Oracle). - Identifier quoting: backticks (MySQL/BigQuery) vs double quotes (standard) vs brackets (SQL Server).
- Upserts:
ON DUPLICATE KEY UPDATE(MySQL) vsON CONFLICT(Postgres/ SQLite) vsMERGE(SQL Server/Oracle). - Functions:
NVL(Oracle) vsISNULL(SQL Server) vsIFNULL(MySQL) vs the standardCOALESCE;GROUP_CONCATvsSTRING_AGGvsLISTAGG.
SQL interview questions
Common questions worth being able to answer crisply:
- What's the difference between WHERE and HAVING?
WHEREfilters rows before grouping;HAVINGfilters groups after aggregation. - INNER vs LEFT JOIN? INNER keeps only matching rows; LEFT keeps all left rows, NULL-filling unmatched right columns.
- How do you find duplicate rows?
GROUP BYthe columns and addHAVING COUNT(*) > 1. - Get the second-highest salary? Use a window function:
ROW_NUMBER() OVER (ORDER BY salary DESC)and filter for rank 2, or a correlated subquery. - What is a CTE and when is it better than a subquery? A named, top-level temporary result set (
WITH) that improves readability, can be referenced multiple times, and supports recursion. - What makes a query non-sargable? Applying a function or implicit cast to an indexed column in the WHERE clause, which prevents index use.
- UNION vs UNION ALL?
UNIONremoves duplicates (an extra sort/hash);UNION ALLkeeps them and is faster. - How do transactions guarantee correctness? Via ACID — Atomicity, Consistency, Isolation, Durability.
SQL cheat sheet
Basic SELECT
SELECT col1, col2 FROM table WHERE col1 = 'x' ORDER BY col2 DESC LIMIT 10;
Aggregate + GROUP BY
SELECT dept, COUNT(*), AVG(salary) FROM employees GROUP BY dept HAVING COUNT(*) > 5;
JOIN
SELECT o.id, c.name FROM orders o JOIN customers c ON c.id = o.customer_id;
Window function
SELECT name, salary,
RANK() OVER (
PARTITION BY dept
ORDER BY salary DESC) AS r
FROM employees;CTE
WITH recent AS ( SELECT * FROM orders WHERE created_at > '2024-01-01' ) SELECT * FROM recent;
Upsert (Postgres)
INSERT INTO t (id, n) VALUES (1, 5) ON CONFLICT (id) DO UPDATE SET n = EXCLUDED.n;
How to use this SQL tool
- Paste or upload your SQL (or click "Load sample"). Everything stays in your browser.
- Format & validate with one click — pick your dialect, keyword case and indentation.
- Lint to surface security, performance and style issues, then apply one-click fixes.
- Score the query for an at-a-glance 0–100 health rating with actionable notes.
- Check compatibility across MySQL, PostgreSQL, SQL Server, Oracle, SQLite, MariaDB, Snowflake, BigQuery and Redshift.
- Analyze the execution plan by pasting
EXPLAINoutput to find bottlenecks and missing indexes. - Document & export schema docs and reports to Markdown, HTML, JSON or CSV.
Frequently asked questions
A SQL formatter (or beautifier) takes raw, minified or inconsistently-styled SQL and re-prints it with consistent indentation, keyword casing and line breaks so the structure of the query — its clauses, joins and subqueries — becomes easy to read. This tool also validates, lints, scores and analyzes the query, all locally in your browser.
Paste, type or upload your SQL into the editor and click Format (or press Ctrl/Cmd + Shift + F). The tool tokenizes the query, then re-indents it according to your chosen dialect, keyword case (UPPER, lower or preserve) and indentation (2 spaces, 4 spaces or tabs). Everything runs client-side, so your SQL is never uploaded.
Formatting and analysis work with Standard SQL, MySQL, MariaDB, PostgreSQL, SQL Server (T-SQL), Oracle (PL/SQL), SQLite, Snowflake, BigQuery and Redshift. The Compatibility tab additionally checks a single query against all of these databases and flags dialect-specific syntax that will not port.
The linter runs dozens of rules across six categories: naming conventions (consistent identifier casing, reserved words as aliases), formatting (keyword case, trailing whitespace, indentation), anti-patterns (implicit comma joins, NOT IN with NULLs, ORDER BY ordinals), performance (SELECT *, leading-wildcard LIKE, non-sargable functions on columns), security (UPDATE/DELETE without WHERE, GRANT ALL, string-concatenation injection smells) and maintainability (deep nesting, missing semicolons). Each finding has a severity and, where safe, a one-click fix.
The overall 0–100 health score is a weighted blend of five sub-scores: Security (28%), Performance (24%), Maintainability (20%), Readability (16%) and Complexity (12%). Each sub-score starts at 100 and is reduced by the linter findings and structural signals (joins, subqueries, nesting depth, SELECT *, line length). The score is deterministic — the same query always yields the same result.
Completely. All tokenizing, formatting, validation, linting, scoring, compatibility checking and execution-plan analysis happen locally in your browser using JavaScript. Your SQL is never uploaded to a server, never logged and never stored remotely. The tool also works offline once the page has loaded.
Click Minify (or Ctrl/Cmd + Shift + M). The tool removes comments and collapses all non-significant whitespace onto a single line while preserving string literals exactly, then reports the original size, the minified size and the percentage saved — handy for embedding queries in code or logging.
Formatting (beautifying) adds whitespace and line breaks to make a query readable by humans. Minifying does the opposite — it strips comments and redundant whitespace to make the query as small as possible for storage or transport. Both preserve the meaning of the SQL exactly.
Yes, in two ways. The linter flags query-text anti-patterns that commonly cause slow queries (full-scan-inducing SELECT *, leading-wildcard LIKE, functions wrapped around indexed columns, comma joins). The Execution Plan analyzer goes deeper: paste the output of EXPLAIN / EXPLAIN ANALYZE (PostgreSQL, MySQL or SQLite) and it identifies sequential scans, costly nested loops, filesorts and temp tables, then recommends indexes.
Run EXPLAIN (or EXPLAIN ANALYZE / EXPLAIN FORMAT=JSON) in your database, copy the output, and paste it into the Execution Plan tab. The analyzer parses PostgreSQL text and JSON plans, MySQL JSON and tabular plans, and generic plans, builds a node tree, highlights the costliest operations, and lists concrete recommendations such as which columns to index.
A predicate is “sargable” (Search-ARGument-able) when the database can use an index to satisfy it. Wrapping an indexed column in a function — e.g. WHERE YEAR(created_at) = 2024 or WHERE LOWER(email) = ... — makes the predicate non-sargable, forcing a full scan. The linter detects this and suggests rewriting it as a range condition or adding a functional index.
SELECT * fetches every column, which increases I/O and network transfer, prevents the optimiser from using covering indexes, and silently breaks application code when columns are added or reordered. Listing the specific columns you need is faster, safer and self-documenting. The linter flags SELECT * automatically.
Yes. The Compatibility tab scans your query for dialect-specific features — LIMIT vs TOP, AUTO_INCREMENT vs SERIAL vs IDENTITY, backtick vs bracket quoting, ILIKE, NVL, GROUP_CONCAT, ON DUPLICATE KEY vs ON CONFLICT vs MERGE, and more — and shows, for each of nine databases, whether the feature is fully supported, partially supported or unsupported.
Yes. The Documentation tab reads your CREATE TABLE statements and produces structured schema docs — columns, types, constraints, primary keys and foreign keys — and a plain-English explanation of what each query does. You can export everything to Markdown, HTML or JSON.
Yes. The formatter and linter operate on the raw token stream, so they handle DDL (CREATE TABLE/VIEW/INDEX), stored procedures, functions and triggers in addition to SELECT/INSERT/UPDATE/DELETE. Use the Templates library for ready-made procedure, function and trigger scaffolds.
Yes. The tool is 100% free with no sign-up, no usage limits and no watermarks. Format, validate, lint, score, analyze and document as much SQL as you like, as often as you like.
Ctrl/Cmd + Shift + F formats, Ctrl/Cmd + Shift + M minifies, Ctrl/Cmd + K clears the editor, Ctrl/Cmd + S downloads the SQL, and Ctrl/Cmd + / opens the shortcuts panel. Native undo/redo (Ctrl/Cmd + Z / Y) works inside the editor.
Yes. Because all processing is client-side, formatting, validation and analysis keep working without a connection once the page has loaded. The interface is fully responsive and optimized for phones, tablets and desktops.
Both produce an intermediate result set. A subquery is nested inline inside another query; a Common Table Expression (CTE) is defined up front with WITH name AS (…) and then referenced by name. CTEs read top-to-bottom like steps, can be referenced multiple times, and support recursion — which usually makes complex queries more readable and maintainable. The complexity score rewards using CTEs to flatten deep nesting.
Related Tools
Explore More Tools
Go ad-free & unlock power features
- Zero ads, faster focused workflow
- Upload 50MB+ files & batch process
- Priority AI type & schema generation