SQL Query Tester

Paste a query and read back its brackets, clause order, tables, joins, and the patterns that tend to make a statement slow. Everything runs on the text in the editor, so no database is touched and nothing leaves the tab.

Query

MySQL

Analysis

Ready

Type a query on the left. Results land here as you go.

-Statement
0Tables
0Joins
-Complexity
Load a query

Nothing here connects to a database

The analyser reads the text sitting in the editor. It opens no connection, sends the statement nowhere, and has never seen your schema. Every line of output comes from pattern matching over the SQL you typed, which is why a query against a table that does not exist still returns a clean result, and a misspelled column name goes unmentioned.

That trade buys three things a live connection would not. The query stays inside your browser, so a statement pulled from a client database is safe to paste. A DELETE with no WHERE gets flagged instead of run. And you get an answer on a machine with no database installed at all.

ControlWhat it does to your query
Analyze queryRuns every check the current settings allow. With Analyze as I type ticked it also fires on its own a moment after you stop typing, so the button is there for when you switch that off.
DialectSelects which engine-specific rules apply. The tag above the editor mirrors the choice so a stale setting stays visible.
Expected statementSet it to SELECT, UPDATE, or any other verb and the analyser reports a mismatch. Handy against a template that assembles the statement in pieces, where an UPDATE arriving instead of a SELECT means a bug upstream. Leave it on Any to skip the check.
ChecksThree levels. Syntax only runs the shared structural rules, syntax and dialect adds the engine rules, and the third level adds the performance pass.
ExplainSwaps the panel for a plain reading of the statement: its verb, its effect on stored data, the tables and joins found. No execution plan is involved, since that comes from the database engine.
FormatRebuilds the statement with one clause per line. Read the caution further down before pasting the result back into code.
Copy and ClearCopy puts the editor contents on your clipboard. Clear empties the editor and resets the four counters.

The four sample queries below the panels load in one click. The last two are worth a look before you paste anything of your own, since they show what a flagged query and a broken query each read like.

What the syntax check catches, and what slips past

Four checks run on every statement, whatever the dialect:

Plenty gets through. A missing comma between two column names, a keyword spelled wrong, a join condition pointing at the wrong key, an aggregate with no matching GROUP BY entry: none of those trip a rule here. Treat a clean result as evidence the shape of the statement holds together, not as proof the database will accept it.

Dialect rules are narrow, and deliberately so

Picking a dialect adds a small set of extra checks on top of the shared ones, from the second Checks level upward. The list is short because each rule has to hold without a schema in front of it.

DialectExtra rule applied
MySQLA LIMIT and OFFSET pair that does not read as LIMIT 10 OFFSET 20, which catches the split forms borrowed from other engines.
PostgreSQLBacktick identifiers, a MySQL habit that is a syntax error in Postgres. Also IFNULL, which Postgres spells COALESCE.
SQLiteAUTO_INCREMENT, since SQLite writes AUTOINCREMENT with no underscore. Also RIGHT JOIN, which needs SQLite 3.39 or newer.
SQL ServerLIMIT, which does not exist there. The equivalents are TOP n and OFFSET n ROWS FETCH NEXT m ROWS ONLY.
OracleLIMIT again, replaced by FETCH FIRST n ROWS ONLY from 12c onward. Lower case inside a double quoted identifier is flagged too, since Oracle folds unquoted names to upper case and quoted ones stay exact.
Standard SQLSkips the dialect pass entirely. Pick this one when the query has to stay portable across engines.

One quirk worth knowing: these scans read the whole statement as flat text. A backtick living inside a quoted string still trips the PostgreSQL note, and AUTO_INCREMENT written inside a comment still trips the SQLite one. False positives are the price of a checker with no parse tree.

How the complexity score is built

The Complexity counter is a weighted count of constructs found in the text, and the panel prints the raw score next to the label. Knowing the weights makes both readable rather than mysterious.

ConstructPoints eachReasoning
Subquery, window function3Each one adds a result set the engine builds before the outer query proceeds.
Join, aggregate, CTE2Joins multiply the row space. Aggregates and CTEs add a grouping or materialisation step.
AND or OR in a condition1A rough stand-in for how much filtering logic the planner has to reason about.

The total maps to four labels: Low up to 5, Medium up to 15, High up to 30, and Very High above that. The query loaded on arrival scores 11 and lands in Medium, from two joins, three aggregate calls, and one condition. Load the CTE sample to watch a window function and a WITH clause push the same statement higher.

The number describes the statement, never the data underneath it. A three-way join across tables of four hundred rows scores the same as one across four hundred million. One unindexed join beats ten indexed ones for real cost every time. Read the score as a measure of how hard the query is for a person to review, and take timing from the database itself.

The performance warnings and the reasoning behind each

Set Checks to Add performance pass to switch this on. The two lower levels stay with syntax and dialect, which is why a query holding an obvious SELECT * reports clean on the default setting. Results split into two groups: warnings for the two patterns that carry real risk, and a worth timing group for the rest.

Every one of these is a heuristic. SELECT * in a one-row lookup by primary key costs nothing worth measuring, and a leading wildcard over a two hundred row lookup table is fine. The warnings mark places to think, not defects to fix on sight.

Format moves line breaks, it does not parse. It collapses the statement to a single line, then breaks before each clause keyword and each AND or OR. Column lists stay intact, which the older version of this page did not manage. What it still gets wrong is text inside quotes: a string literal holding the word AND, or a multi-line literal you wanted left alone, comes back split and upper cased. Use it on a wall of text you are reading. For a query going back into a codebase, keep the original or run it through the dedicated SQL formatter.

The query is valid and still slow. What next

This page reaches its limit once the syntax holds and the warnings are addressed. Real cost lives in the execution plan, which only the engine holding your data produces. Run EXPLAIN in MySQL or EXPLAIN ANALYZE in PostgreSQL against the query and read three things: whether each table is seeked or scanned, the gap between estimated and actual row counts, and which step holds the largest share of total time. A planner estimating 40 rows where 900,000 arrive points at stale statistics, and no amount of query rewriting fixes that.

For tracking timings across a batch of statements rather than checking one, the database query profiler is built for that shape of work. Building the statement from scratch instead of debugging one belongs in the SQL query builder.

Questions that come up with a query already pasted in

The behaviour behind the results, including the parts that surprise people.

Does this page run my SQL query?

No. There is no database behind it and no connection is opened. The analyser reads the text you typed, so it reports on structure and known slow patterns rather than on rows returned. A query referencing tables that do not exist still returns a result here.

Is my query uploaded anywhere?

No. The editor and every check run in page JavaScript inside your browser. Nothing is transmitted and nothing persists once the tab closes, which makes a statement copied from a production system safe to paste. Save anything you want to keep before leaving.

Does it check the query while I type?

Yes, a short pause after your last keystroke triggers a run. Untick Analyze as I type on a long statement you are midway through editing, since a half written query reports errors that resolve themselves once you finish. The Analyze query button then runs it on demand.

Why do I get no performance warnings at all?

That pass only runs on the third Checks level, Add performance pass. The two below it stay with syntax and dialect rules. Switch the level to see the SELECT star, missing WHERE, wildcard LIKE, and unbounded ORDER BY findings.

What is the Expected statement setting for?

It asserts which verb the query should open with. Set it to SELECT while testing a read path and any INSERT or UPDATE that arrives gets reported as a mismatch, which catches a template assembling the wrong branch. A WITH clause counts as a SELECT. Leave it on Any to turn the check off.

Which dialects have rules of their own?

All five. MySQL checks LIMIT and OFFSET form, PostgreSQL flags backticks and IFNULL, SQLite flags AUTO_INCREMENT and RIGHT JOIN, SQL Server and Oracle both flag LIMIT and name their own replacement. Standard SQL skips the dialect pass, and so does the syntax only level.

What does the complexity label measure?

A weighted count of constructs in the text. Subqueries and window functions score 3, joins and aggregates and CTEs score 2, each AND or OR scores 1. Under 6 is Low, under 16 is Medium, under 31 is High. Table size and indexes play no part in it.

Why did Format change text inside my quotes?

Formatting matches keywords as plain text with no parse tree behind it, so a string literal holding the word AND picks up a line break and an upper cased keyword. Column lists and function arguments survive intact. Use it for reading a dense statement, not for rewriting a source file.

Does it check my table and column names?

No. Names are extracted from FROM, JOIN, UPDATE, and INTO clauses so the stat cards stay accurate, and they are never verified against a schema. A typo in a column name passes every check on this page and fails the moment the database sees it.