MySQL vs PostgreSQL vs SQL Server: Formatting Differences
SQL formatting across MySQL, PostgreSQL, and SQL Server. Quoting, LIMIT vs TOP, concatenation, auto-increment, boolean types, and case sensitivity examples.
Same Language, Different Dialects
SQL is a standard, but no database implements it identically. The formatting differences are small but persistent. When you switch between MySQL, PostgreSQL, and SQL Server, the same query often needs syntax adjustments. A formatter that knows the dialect applies the right rules automatically.
Identifier Quoting
| Database | Quote Character | Example |
|---|---|---|
| MySQL | Backtick | `user_id` |
| PostgreSQL | Double quote | "user_id" |
| SQL Server | Square brackets (default) | [user_id] |
All three support the SQL standard double-quote, but default conventions differ. A formatter set to the correct dialect applies the right quoting style to reserved word collisions.
When you name a column order or a table group, the formatter adds the
dialect-appropriate quotes. In MySQL: `order`. In PostgreSQL: "order".
In SQL Server: [order]. Without quotes, those identifiers parse as keywords
and the query fails.
String Literals and Concatenation
| Database | Escape | Concatenation |
|---|---|---|
| MySQL | 'it''s' or "it's" (non-ANSI mode) | CONCAT(a, b) |
| PostgreSQL | 'it''s' or $$it's$$ (dollar quoting) | `a |
| SQL Server | 'it''s' | a + b or CONCAT(a, b) |
String concatenation is the most visible difference. 'Hello ' || name works
in PostgreSQL. 'Hello ' + name works in SQL Server. MySQL uses CONCAT().
PostgreSQL's dollar quoting ($$...$$) lets you embed single quotes without
escaping. This is especially useful in function bodies and dynamic SQL:
-- PostgreSQL dollar quoting
SELECT $$It's a "quoted" string$$;
-- No escaping needed for single or double quotes
-- Equivalent with standard escaping
SELECT 'It''s a "quoted" string';
Limiting Results
-- MySQL / PostgreSQL
SELECT * FROM users LIMIT 10;
-- SQL Server
SELECT TOP 10 * FROM users;
-- or (SQL Server 2012+)
SELECT * FROM users OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY;
LIMIT is the de facto standard (MySQL, PostgreSQL, SQLite). SQL Server uses
TOP or the OFFSET...FETCH syntax, which is the SQL:2008 standard. A
formatter keeps these dialect-specific keywords as-is.
When you format a query with LIMIT and switch the dialect to SQL Server, the
formatter does not auto-convert LIMIT to TOP. It preserves the intent. You
must rewrite the query manually for cross-database portability.
Pagination Differences
-- MySQL / PostgreSQL: LIMIT with OFFSET
SELECT * FROM users ORDER BY id LIMIT 20 OFFSET 40;
-- SQL Server: OFFSET...FETCH
SELECT * FROM users ORDER BY id OFFSET 40 ROWS FETCH NEXT 20 ROWS ONLY;
-- MySQL also supports shorthand
SELECT * FROM users ORDER BY id LIMIT 40, 20;
-- LIMIT offset, count
The MySQL shorthand LIMIT offset, count reverses the argument order from the
standard OFFSET...LIMIT. If you format a MySQL query with the shorthand, the
formatter preserves it as-is.
Auto-Increment Columns
-- MySQL
id INT AUTO_INCREMENT PRIMARY KEY
-- PostgreSQL
id SERIAL PRIMARY KEY
-- or (PG 10+)
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
-- SQL Server
id INT IDENTITY(1,1) PRIMARY KEY
SERIAL in PostgreSQL is syntactic sugar for creating a sequence and setting
the default. GENERATED ALWAYS AS IDENTITY is the SQL-standard approach and
preferred for new projects. IDENTITY(1,1) in SQL Server means start at 1,
increment by 1.
Boolean Types
| Database | Boolean Type |
|---|---|
| MySQL | BOOLEAN (alias for TINYINT(1)) |
| PostgreSQL | BOOLEAN (native type) |
| SQL Server | BIT |
PostgreSQL has a true boolean type with TRUE, FALSE, and NULL. You can
write WHERE active instead of WHERE active = TRUE. MySQL's BOOLEAN is a
TINYINT and accepts 0 and 1 (and other integers). SQL Server uses BIT
(0, 1, or NULL).
These differences affect how you write conditions:
-- PostgreSQL: native boolean
SELECT * FROM users WHERE active;
SELECT * FROM users WHERE NOT active;
-- MySQL: works but treats 0 as false, non-zero as true
SELECT * FROM users WHERE active;
SELECT * FROM users WHERE NOT active;
-- SQL Server: BIT requires explicit comparison
SELECT * FROM users WHERE active = 1;
SELECT * FROM users WHERE active = 0;
Case Sensitivity
| Database | Default Case Sensitivity |
|---|---|
| MySQL | Case-insensitive (depends on collation) |
| PostgreSQL | Case-sensitive (lowercase unless quoted) |
| SQL Server | Case-insensitive (depends on collation) |
In PostgreSQL, SELECT * FROM Users fails if the table was created as users
(unquoted identifiers are folded to lowercase). In MySQL and SQL Server, case
of unquoted identifiers doesn't matter by default. This affects how you format
table and column references.
If you create a table in PostgreSQL with CREATE TABLE "Users", you must
reference it as "Users" (with quotes, exact case) forever. Avoid quoted
identifiers in PostgreSQL unless you have a specific reason. Use snake_case
for all names.
Function Names
SQL functions (COUNT, SUM, AVG, COALESCE) are case-insensitive in all
three databases. The SQL Formatter uppercases them for
readability, and this is safe across dialects.
Vendor-specific functions differ:
| MySQL | PostgreSQL | SQL Server |
|---|---|---|
NOW() | NOW() | GETDATE() |
IFNULL(a, b) | COALESCE(a, b) | ISNULL(a, b) |
GROUP_CONCAT() | STRING_AGG() | STRING_AGG() |
REGEXP | ~ | LIKE with patterns |
IF(cond, a, b) | CASE WHEN cond THEN a ELSE b END | IIF(cond, a, b) |
DATE_FORMAT() | TO_CHAR() | FORMAT() |
When formatting, these vendor-specific functions are preserved as-is. The
formatter does not convert GETDATE() to NOW(). That's a migration concern,
not a formatting concern.
Data Type Differences
-- MySQL
TEXT, MEDIUMTEXT, LONGTEXT
-- PostgreSQL
TEXT (unlimited) or VARCHAR(n)
-- SQL Server
VARCHAR(MAX) or NVARCHAR(MAX)
PostgreSQL's TEXT type is unlimited. MySQL has multiple text types with
different size limits. SQL Server uses VARCHAR(MAX) for large text.
Comment Styles
All three databases support -- for single-line comments and /* */ for
block comments. MySQL also supports # for single-line comments (non-standard).
A formatter should preserve comment style while ensuring the query remains valid.
-- Standard single-line comment (all databases)
SELECT * FROM users;
/* Block comment
spanning multiple lines (all databases) */
SELECT * FROM orders;
# MySQL-only single-line comment (not portable)
SELECT * FROM products;
Using the Formatter
The SQL Formatter lets you select a dialect before formatting. It preserves dialect-specific keywords (TOP, LIMIT, SERIAL) and adjusts quoting conventions.
Try it yourself: open the SQL Formatter. Switch the dialect between MySQL, PostgreSQL, and SQL Standard. Paste
SELECT `user_id`, `name` FROM `users`(MySQL-style backtick quoting) and format it. Change to PostgreSQL dialect and observe how the formatter handles the backtick quoting in the output. Try a query withLIMITand switch to SQL Server to see how it is preserved.