A reserved word is a keyword the SQL parser needs for its own grammar — SELECT, GROUP, ORDER, TABLE — and none of the five major dialects will let you use one as a column or table name unless you quote it correctly. Skip the quoting, or use the wrong quote character for the dialect you're on, and you get anything from an immediate syntax error to a query that runs cleanly and returns the wrong data.
The trap is that "reserved" doesn't mean the same list twice. PostgreSQL, MySQL, SQLite, SQL Server, and BigQuery each maintain their own reserved-word list, and each escapes a collision with a different quote character — double quotes, backticks, or square brackets. A column called user needs quoting in PostgreSQL and SQL Server, is a completely ordinary identifier in MySQL and BigQuery, and doesn't appear on SQLite's keyword list at all.
This post covers which words are reserved in which dialect, how to quote them correctly, and the specific cases — SQLite's double-quote fallback, MySQL's ANSI_QUOTES mode, SQL Server's QUOTED_IDENTIFIER setting — where the wrong quote mark doesn't error at all. It just silently changes what your statement means.
What makes a word "reserved" instead of just a keyword?
SQL parsers recognize hundreds of keywords, but only some of them are reserved. A non-reserved keyword like DATE or VALUE in PostgreSQL is known to the grammar but can still be used as a column or table name in most positions, because the parser can tell from context which one you mean. A reserved keyword like SELECT or GROUP can't be disambiguated that way — the grammar needs that exact token in that position, so the parser has no way to know whether you meant the keyword or a name.
PostgreSQL's own documentation lists three tiers: reserved, reserved-but-usable-as-a-function-or-type-name, and non-reserved. MySQL and SQL Server publish flatter reserved/non-reserved lists. BigQuery's list is short enough that a word is really just reserved or it isn't. SQLite doesn't separate the two at all — it maintains a single keyword list and recommends quoting any identifier that matches one of them, reserved or not, because the list keeps growing as SQLite adds features.
Why do reserved words break some queries and not others?
The most common failure mode is a hard syntax error — you use order unquoted somewhere PostgreSQL expects a plain identifier, the parser hits the ORDER keyword instead, and the statement refuses to run. That's the easy case: the error points at the exact spot, and quoting or renaming fixes it immediately.
The dangerous case is when the same quote character has two jobs — string literal and identifier — and a session setting decides which one wins. SQLite is the clearest example: if you double-quote a word that turns out not to match any column or table name in scope, SQLite doesn't error. It quietly treats the double-quoted token as a string literal instead, so your WHERE clause silently compares a column to a hardcoded string instead of the identifier you meant.
MySQL and SQL Server have a version of the same footgun through session modes: MySQL's ANSI_QUOTES mode flips double quotes from string literals to identifier quoting, and SQL Server's QUOTED_IDENTIFIER setting does the same thing in reverse, on by default. Code written assuming one setting and run under the other doesn't throw a syntax error — a double-quoted value just silently swaps meaning between "a string" and a column reference.
How do you quote a reserved word in PostgreSQL?
PostgreSQL uses double quotes for delimited identifiers. Wrap the reserved word in double quotes and reference it that way everywhere it appears.
Quoting a PostgreSQL identifier also makes it case-sensitive — "order" and "Order" become different identifiers, so you have to quote it exactly the same way in every reference. PostgreSQL's key words appendix lists every reserved and non-reserved word by version — check it before you name a column, not after the deploy fails.
How do you escape a reserved keyword in MySQL?
MySQL's default identifier quote character is the backtick, not a double quote. Wrap the reserved word in backticks and reference it the same way everywhere the name is used.
This one has a version trap: rank, range, and rows were ordinary identifiers before MySQL 8.0 added window functions, then became reserved words in 8.0.2 to support that syntax. A column literally named rank that worked fine on MySQL 5.7 throws a syntax error the moment you upgrade to 8.0 — with no code change on your end.
MySQL also has an ANSI_QUOTES SQL mode that turns double quotes into identifier delimiters instead of string delimiters; see our MySQL vs PostgreSQL syntax differences guide for how the two dialects diverge on quoting more broadly, and check the MySQL keywords and reserved words reference before naming anything after a window-function term.
How do you quote a reserved keyword in SQL Server (T-SQL)?
SQL Server's standard way to escape a reserved keyword is square brackets. This works regardless of session settings, which is why most T-SQL style guides prefer brackets over quotes.
Double quotes also work as identifier delimiters in T-SQL, but only when QUOTED_IDENTIFIER is ON — the default, but a setting individual connections or legacy scripts can override. Because brackets aren't affected by that setting, they're the more portable choice inside stored procedures and scripts you don't fully control. See Microsoft's reserved keywords reference for the full T-SQL, ODBC, and future-keyword lists — the future-keywords table is worth checking if you're naming something after a newer SQL standard term.
How does SQLite handle reserved keywords?
SQLite is the most permissive dialect here: it accepts four quoting styles — double quotes, backticks, square brackets, and (with caveats) single quotes — so code migrated from any other dialect tends to just work. SQLite doesn't separate "reserved" from "non-reserved" either; its keyword list is a single list of roughly 147 words, and the docs recommend quoting anything on it, not just the words that currently cause errors.
Stick to double quotes if you're writing new SQLite code — it's the only one of the four that's actually standard SQL, and it sidesteps the double-quote-as-string-literal fallback described above by making sure the name really does resolve to a column or table. Our SQLite syntax error guide walks through the other common causes of SQLite's "near X: syntax error", including this one.
How do you quote a reserved word in BigQuery?
BigQuery's GoogleSQL dialect uses backticks, the same character as MySQL. Wrap the reserved word and reference it consistently.
BigQuery's reserved-word list is also unusually short — roughly 80 keywords, versus PostgreSQL's several hundred. Common column names like user, table, value, and key aren't on it, so they work as plain identifiers with no quoting at all; only structural keywords like SELECT, FROM, GROUP, ORDER, and LIMIT are actually reserved. Check Google's lexical structure and syntax reference for the full list before assuming a word is safe, and see our BigQuery syntax errors guide for the other ways reserved words show up as parser errors there.
Which reserved words trip people up across every dialect?
A handful of words are worth memorizing because they're reserved almost everywhere, and a few are worth memorizing precisely because they aren't:
SELECT, FROM, WHERE, GROUP, and ORDER — reserved in all five dialects. Never use these unquoted as identifiers, in any dialect.
TABLE — reserved in PostgreSQL, MySQL, SQL Server, and SQLite; not reserved in BigQuery.
USER — reserved in PostgreSQL and SQL Server; an ordinary identifier in MySQL, SQLite, and BigQuery.
KEY — reserved in MySQL, SQL Server, and SQLite because of PRIMARY KEY / FOREIGN KEY syntax.
RANK, RANGE, and ROWS — reserved in MySQL only since version 8.0.2, and reserved in SQLite and BigQuery as window-function syntax; check your MySQL version before assuming any of these three are safe.
Before you name a column or table, check these things
Cross-check the identifier against your dialect's own reserved-word list, not a generic one — the same word can be reserved in one dialect and free in another.
If it's reserved, quote every reference to it consistently: double quotes in PostgreSQL, backticks in MySQL and BigQuery, brackets in SQL Server, and preferably double quotes in SQLite.
When you have the choice, rename instead of quoting — quoted identifiers are easy to reference inconsistently across a codebase, and most "quoting" bugs are typos in the quoting, not the name.
Don't rely on session settings like MySQL's ANSI_QUOTES or SQL Server's QUOTED_IDENTIFIER to make double quotes behave — assume the default and use the dialect's dedicated identifier-quote character instead.
Re-check reserved-word lists after a major version upgrade. MySQL's window-function keywords (RANK, RANGE, ROWS) are the textbook example of a name that was safe and stopped being safe.
Test the exact statement that failed, not just the identifier in isolation — a reserved word can be legal as a column alias in one clause and illegal as a bare reference in another.
Run schema and query changes through a validator that understands your target dialect before they reach production, so a missed reserved-word collision shows up as a syntax error in review instead of a broken deploy.
Wrapping up
Reserved words aren't a bug to work around case by case — they're a fixed, dialect-specific list, and the fix is almost always the same: quote consistently or rename. The five dialects don't even agree on which words are reserved, let alone how to escape them, so the safest habit is to treat every identifier as a candidate collision and check it against the dialect you're actually shipping to.
If you'd rather not memorize five separate keyword lists, paste the query into sqlfmt — it validates syntax across all five dialects covered here and flags a reserved-word collision as a syntax error before it reaches production. The free tier covers three dialects; pro unlocks all five, plus live-as-you-type checking.