Skip to content

Expert: design, speed and engines · Procedures, triggers, SQL engines and NoSQL · topic 54 of 62 · 35 min

MySQL vs PostgreSQL vs SQLite vs SQL Server

Explanation

All four speak SQL, but they differ in licence, typical use and many small details: how you make an auto-increment key, how you limit rows, join text, upsert, store true/false and compare text. Most of what you learned in SQLite works everywhere; knowing the differences saves you hours when you change jobs or engines.

Why the differences matter

SQL is a standard, but every engine adds its own words and leaves out others. These local versions are called dialects. Your SELECT, WHERE, JOIN, GROUP BY and window functions will work almost unchanged across engines. The surprises are in the edges: keys, limits, dates, text and upserts. Knowing them helps in interviews and stops "it worked on my laptop" bugs.

Who makes them and where they are used

  • MySQL: owned by Oracle. The Community Edition is open source under the GPL; paid editions add support and tools. Very common for websites and PHP apps (WordPress), and in many Indian startups. MariaDB is a community fork.
  • PostgreSQL: open source under the permissive PostgreSQL Licence, built by a global community. Known for strict standards, rich types (JSONB, arrays) and extensions such as PostGIS. A frequent default for new backends.
  • SQLite: public domain, a library inside your app with no server. Used in every Android and iOS phone, browsers, desktop apps and small tools. Not meant for many writers at once over a network.
  • SQL Server: Microsoft's commercial database, with free Express and Developer editions. Common in banks, government projects and companies built on .NET and Windows.

The comparison table

FeatureMySQLPostgreSQLSQLiteSQL Server
Auto-increment keyid INT AUTO_INCREMENT PRIMARY KEYid SERIAL, or GENERATED ALWAYS AS IDENTITY (10+)id INTEGER PRIMARY KEYid INT IDENTITY(1,1) PRIMARY KEY
First 10 rowsLIMIT 10LIMIT 10 (or FETCH FIRST 10 ROWS ONLY)LIMIT 10TOP 10, or OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
Join textCONCAT(a, ' ', b)a || ' ' || b, or CONCATa || ' ' || ba + ' ' + b, or CONCAT
UpsertINSERT ... ON DUPLICATE KEY UPDATEINSERT ... ON CONFLICT (col) DO UPDATE; MERGE (15+)INSERT ... ON CONFLICT (col) DO UPDATEMERGE
True/false typeBOOLEAN = TINYINT(1), stores 0/1Real BOOLEANNo boolean type; stores 0/1BIT (0/1)
Current timeNOW()now(), CURRENT_TIMESTAMPdatetime('now')GETDATE(), SYSDATETIME()
Date mathsDATE_ADD(d, INTERVAL 7 DAY)d + INTERVAL '7 days'date(d, '+7 days')DATEADD(day, 7, d)
'pune' = 'Pune'True with default collationsFalse (use ILIKE or lower())False (LIKE ignores A–Z case)Depends on collation; the default is case-insensitive
FULL OUTER JOINNo (emulate with LEFT UNION RIGHT)YesYes (3.39+)Yes
JSONJSON type, ->, ->>json and jsonb, ->, ->>, @>, indexablejson_extract, ->, ->> on textJSON_VALUE, OPENJSON on text
Quote a name`order`"order""order"[order]

A few notes on the table. In MySQL, || means OR by default, not "join text", which surprises people coming from PostgreSQL. In SQLite, a column declared INTEGER PRIMARY KEY becomes the row id and fills itself in; adding AUTOINCREMENT only stops old ids from being reused. In SQL Server, OFFSET ... FETCH needs an ORDER BY.

Oracle in one paragraph

Oracle Database, from Oracle Corporation, is the long-time leader in large enterprises, banks and telecom companies. It is commercial (a free edition exists for learning). Its dialect has some habits worth knowing: SELECT needs FROM dual when there is no table, text columns are usually VARCHAR2, an empty string '' is treated as NULL, procedures are written in PL/SQL, and row limits use FETCH FIRST n ROWS ONLY (12c and later) or the older ROWNUM. Check the current Oracle docs for newer features.

Writing portable SQL

Portable SQL runs on many engines with no change. It matters for libraries, tools and teams that might switch engines. A few habits get you most of the way:

  • Prefer standard functions: COALESCE instead of IFNULL, ISNULL or NVL; CASE instead of IF() or IIF(); CAST(x AS ...) instead of engine-only casts.
  • Use window functions (ROW_NUMBER) for top-N when you must avoid LIMIT and TOP; they work in all four engines today.
  • Write explicit JOIN ... ON, and name every column in INSERT.
  • Avoid reserved words such as order, user and date as names, so you never need backticks, quotes or brackets.
  • Store dates as real date types (or YYYY-MM-DD text in SQLite) and keep date maths in one place.
sql
SELECT name, price
FROM (SELECT name, price, ROW_NUMBER() OVER (ORDER BY price DESC) AS rn
      FROM products) AS ranked
WHERE rn <= 3
ORDER BY price DESC;
-- Running Shoes      | 2499
-- Bluetooth Earbuds  | 1999
-- Pressure Cooker 3L | 1450
Top 3 without LIMIT or TOP: this runs on MySQL 8, PostgreSQL, SQLite and SQL Server.

Common mistakes

  • Using || to join text in MySQL and getting 0 or 1. Fix: use CONCAT(), which works in all four engines.
  • Assuming 'pune' = 'Pune' behaves the same everywhere. Fix: compare lower(city) = 'pune' when case must not matter, and know your collation.
  • Copying LIMIT into SQL Server. Fix: TOP n or OFFSET ... FETCH with ORDER BY.
  • Expecting FULL OUTER JOIN in MySQL. Fix: LEFT JOIN UNION RIGHT JOIN.
  • Testing on SQLite and deploying on PostgreSQL without running the tests on PostgreSQL. Fix: test on the engine you deploy to.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

MySQL vs PostgreSQL vs SQLite vs SQL Server · 1/8

Four engines, one language, many dialects

All four big SQL engines speak SQL, but each has its own dialect. Let us compare MySQL, PostgreSQL, SQLite and SQL Server.

Prefer a video by a teacher? Find free videos on “mysql vs postgresql vs sqlite vs sql server differences” on YouTube ↗

In real life: Moving a startup from MySQL to PostgreSQL

A typical migration story: a young company starts on MySQL, later wants PostgreSQL features such as JSONB indexes or PostGIS for delivery maps, and moves. The data copy is the easy part (tools exist). The work is in the SQL: backtick-quoted names, ON DUPLICATE KEY UPDATE upserts, case-insensitive comparisons that suddenly become case-sensitive, and date functions. Teams that wrote mostly portable SQL move in days instead of months.

sql
-- MySQL
INSERT INTO stock (sku, qty) VALUES ('RICE5', 10)
ON DUPLICATE KEY UPDATE qty = qty + VALUES(qty);

-- PostgreSQL (and SQLite)
INSERT INTO stock (sku, qty) VALUES ('RICE5', 10)
ON CONFLICT (sku) DO UPDATE SET qty = stock.qty + EXCLUDED.qty;
The same upsert in two dialects. Newer MySQL versions prefer a row alias instead of VALUES(); check the current docs.

Practice (0/4 done)

Task 1 · Question

Which engine would you pick for an offline-first Android app that stores the user's notes on the phone?

Task 2 · Question

In MySQL with default settings, SELECT 'Asha' || 'Verma'; does not return 'AshaVerma'. Which query does, and also works in PostgreSQL, SQLite and SQL Server?

Task 3 · Code · SQL

Write portable SQL (no IFNULL, ISNULL or NVL). From customers, return name and email, but show 'not given' when email is NULL. Name the second column email.

Task 4 · Code · SQL

Write a portable top-3 query without LIMIT or TOP: use ROW_NUMBER() OVER (ORDER BY price DESC) in a subquery and keep rows numbered 1 to 3. Return name and price of the 3 most expensive products, most expensive first.

Quick quiz

Question 1

1.How do you return only the first 5 rows in SQL Server?

Question 2

2.Which engine does NOT support FULL OUTER JOIN?

Question 3

3.With default settings, where is WHERE city = 'pune' likely to match a row whose city is 'Pune'?

Question 4

4.How do you quote a column named order (a reserved word) in SQL Server?

Question 5

5.Which statement about booleans is right?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

How do you return only the first 5 rows in SQL Server?

Answer: SELECT TOP 5 ...

SQL Server uses TOP, or OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY with ORDER BY.

Which engine does NOT support FULL OUTER JOIN?

Answer: MySQL

MySQL lacks FULL OUTER JOIN; combine a LEFT JOIN and a RIGHT JOIN with UNION.

With default settings, where is WHERE city = 'pune' likely to match a row whose city is 'Pune'?

Answer: MySQL

MySQL's default collations are case-insensitive. PostgreSQL and SQLite compare text case-sensitively with =.

How do you quote a column named order (a reserved word) in SQL Server?

Answer: [order]

SQL Server uses square brackets (and double quotes when QUOTED_IDENTIFIER is on). MySQL uses backticks; PostgreSQL and SQLite use double quotes.

Which statement about booleans is right?

Answer: PostgreSQL has a real BOOLEAN; SQLite stores 0 and 1; SQL Server uses BIT

MySQL BOOLEAN is TINYINT(1), SQLite has no boolean type, SQL Server uses BIT, PostgreSQL has BOOLEAN.

In SQLite, you write INSERT INTO tickets (customer_id, subject, status) VALUES (3, 'Hi', DEFAULT). What happens?Related

Answer: A syntax error: SQLite does not accept DEFAULT in VALUES

Leave the column out instead. PostgreSQL, MySQL and SQL Server do accept DEFAULT there.

From INSERT

Which is the MySQL way to write an upsert?Related

Answer: INSERT ... ON DUPLICATE KEY UPDATE

MySQL uses ON DUPLICATE KEY UPDATE; SQL Server uses MERGE.

From Upserts: insert or update

Which tool connects to SQLite, PostgreSQL, MySQL and SQL Server from one app?Related

Answer: DBeaver

DBeaver is a universal database tool; psql and pgAdmin are for PostgreSQL, Workbench for MySQL.

From The SQL language and your tools

Which of these is a SQL database?Related

Answer: PostgreSQL

PostgreSQL stores data in tables and uses SQL. MongoDB is a NoSQL document database.

From What is a database?

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 3 more topics in “Procedures, triggers, SQL engines and NoSQL” before its exam: NoSQL: documents, key-value, wide-column and graph, MongoDB and Redis in practice, Choosing a database, and keeping it safe.
  2. Module 11 exam: procedures, triggers, engines and NoSQL · 13 questions · 25 min, without help. 6 topics of this module still to finish first.
  3. Then the next module, Interview questions and practice projects. 62 topics left in the course before the final exam.

← Previous: Triggers