SQL Syntax Checker
Check SQL syntax per dialect — unbalanced quotes, clause order and dialect slips flagged with line and column, entirely on this device.
Checks pasted SQL against the printed rule list below — statement shapes, quoting and clause order per dialect, on this device. Not a full grammar parser and nothing is executed.
Findings appear here as you type.
This checker applies 18 syntax rules:
- Unterminated string literals
- Unterminated quoted identifiers
- Unterminated block comments
- Unterminated dollar quotes (PostgreSQL)
- Unbalanced parentheses and brackets
- Leading, trailing and doubled commas
- Unrecognized statement keywords
- SELECT clause order and empty clauses
- INSERT shape — INTO and a VALUES/SELECT source
- UPDATE shape — SET present, before WHERE
- DELETE shape — FROM required outside SQL Server
- CREATE TABLE shape — name and column list
- Duplicate table aliases in FROM
- Empty IN () lists
- = NULL where IS NULL is meant
- Backtick and [bracket] quoting per dialect
- # comments outside the MySQL family
- LIMIT vs TOP dialect notes
Press ? for shortcuts
Do it offline:$ sqlfluff lint query.sql --dialect postgres
How to use it
- Pick the dialect the paste is written for — backticks are normal in MySQL and flagged in PostgreSQL, [brackets] the other way around.
- Paste one statement or a whole script. Checking runs as you type, statement by statement, and the verdict badge answers within a keystroke pause.
- Read the diagnostics pane: every row quotes its finding with a line:column anchor — click one to land the cursor on that spot.
- Copy or download the findings report — each row keeps its position and message exactly as shown.
Paste SQL and pick a dialect — PostgreSQL, MySQL, SQLite or SQL Server — and this page checks every statement against a printed list of syntax rules: unterminated quotes and comments, unbalanced parentheses, misplaced commas, clause order, duplicate aliases and dialect slips like backticks in PostgreSQL. Each finding lands on its exact line and column, clicking a row jumps to the spot, and your SQL never leaves this device.
Examples
SELECT id FROM users WHERE ORDER BY id;line 1:22 WHERE clause is empty — add a condition or remove it.
A WHERE with nothing in it: the keyword is there, the condition never arrived. The finding anchors at WHERE itself — column 22 — because that is where the fix goes.
SELECT `id`, `name`, FROM users;line 1:20 Comma sits right before FROM — remove it or add the missing item.
Under the MySQL dialect the backticks pass without comment — the one finding is the comma left behind after deleting a column from the select list.
INSERT INTO logs (msg) VALUES ('unclosed);line 1:31 "(" opened here never closes.
line 1:32 String literal opened here never closes — add the closing '.
One missing quote, two truthful findings: the string opened at column 32 swallows the closing parenthesis, so the VALUES parenthesis never closes either. Fix the quote and both rows disappear. The real sqlite3 shell refuses the same paste — executed while building this page.
SELECT TOP 5 * FROM sessions WHERE token = NULL;line 1:42 = NULL never matches — NULL needs IS NULL.
Under the SQL Server dialect TOP 5 is native and passes; the finding is the comparison, which always evaluates to unknown and matches nothing — IS NULL is the form that works. Assignments are exempt: UPDATE … SET token = NULL stays quiet.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Dollar quotes belong to PostgreSQL
$tag$ … $tag$ bodies scan as one string under the PostgreSQL dialect, and an unclosed opener is flagged with its own rule. Under MySQL or SQLite the dollar signs are plain code — the dialect selector changes what the checker may assume.
Backticks, "double quotes" and [brackets] are three dialects' spellings
MySQL quotes identifiers with backticks, PostgreSQL with double quotes, SQL Server with brackets — and SQLite accepts all three. The checker flags the spelling that does not belong to your selected dialect and names the one that does.
An unclosed /* swallows everything after it
A block comment that never closes runs to the end of the input, so later statements vanish from the check rather than erroring individually. PostgreSQL and SQL Server nest /* … */ pairs; MySQL and SQLite close at the first */.
SQLite accepts IN () — most engines refuse it
An empty IN () is a parse error in PostgreSQL and MySQL, but SQLite documents it as legal and evaluates it to false. The checker flags it in every dialect because it never matches a row — the sqlite3 shell run for this page exits cleanly on it, which is exactly why it hides bugs.
A rule list is not a grammar
The printed rule subset catches structural slips — closure, balance, clause order, statement shapes — and nothing more. A paste can pass every rule and still be refused by a real server over a type mismatch or a missing table. Green means no rule fired, not that the statement runs.
Common errors
Input is 1,048,577 characters — the checker accepts at most 1,048,576 (1 MiB).- The size cap refuses outright rather than checking a truncated paste, so a verdict covers your whole input or none of it. The refusal prints the cap and your actual character count; the engine also stops at 500 statements per paste.
String literal opened here never closes — add the closing '.- The most common finding in real pastes — a quote deleted mid-edit. Everything after the opener sits inside the string, semicolons included, so later findings often clear once this one is fixed. Doubled quotes ('it''s') never trip it.
"(" opened here never closes.- Balance is tracked per statement, and the finding anchors at the opener that was never matched — where the missing ) belongs — rather than at the end of the input. An extra closer gets the mirrored message at its own position.
FAQ
Does a clean result mean the query will run?
No. Clean means none of the printed rules fired — the rule list on the tool's empty state is the entire claim. A real server still applies name resolution and types on top, so a missing table or misspelled column is out of scope here. Nothing is executed on this page.
What actually differs between the four dialects?
Lexical rules and statement shapes. Identifier quoting — backticks, double quotes, brackets — is checked against the selected dialect; # line comments are MySQL-family only; dollar quotes are PostgreSQL; LIMIT versus TOP is noted rather than failed; INSERT without INTO and DELETE without FROM are legal in some dialects and flagged in others. The vendor documentation below anchors each row.
Where does my SQL go, and how much can I paste?
Nowhere — checking runs in your browser and the paste is not uploaded. The engine accepts up to 1 MiB and 500 statements per check and lists at most 200 findings, saying so when more were found. The share link carries your SQL and dialect in the URL fragment, so mind credentials before sending one.
Keyboard shortcuts
- /
- Focus the SQL pane.
- ⌘/Ctrl + S
- Download the findings report.
- ⌘/Ctrl + C
- Copy the report while no text is selected.
- ⌘/Ctrl + Shift + C
- Copy the report even mid-selection.
- t
- Advance the theme.
- ?
- Open the shortcut help overlay.
- Esc
- Dismiss the topmost overlay.