MySQL vs PostgreSQL — Key Differences for Schema Design

Both MySQL and PostgreSQL are excellent relational databases, but they have real differences that affect how you design your schema. If you're starting a new project or migrating between them, understanding these differences upfront will save you from surprises later.

At a Glance

FeatureMySQLPostgreSQL
Auto-increment primary keysAUTO_INCREMENTSERIAL or GENERATED ALWAYS AS IDENTITY
BooleansTINYINT(1)Native BOOLEAN type
JSON supportJSON (validated, not indexed)JSON and JSONB (binary, fully indexable)
ArraysNot supported nativelyNative array columns
EnumsBuilt-in ENUM typeCustom types or CHECK constraints
Full-text searchFull-text indexes on InnoDBBuilt-in tsvector with GIN indexes
Default engineInnoDBN/A (one engine)
Case sensitivityTable names case-insensitive on Windows/macOS by defaultAlways case-sensitive (lowercased identifiers)

Auto-Increment Primary Keys

The most immediately visible difference when writing DDL:

-- MySQL
CREATE TABLE users (
    id INT UNSIGNED NOT NULL AUTO_INCREMENT,
    PRIMARY KEY (id)
);

-- PostgreSQL
CREATE TABLE users (
    id SERIAL PRIMARY KEY
    -- or, preferred in modern PostgreSQL:
    -- id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
);

PostgreSQL's SERIAL is syntactic sugar that creates an integer column and a backing sequence object. GENERATED ALWAYS AS IDENTITY (PostgreSQL 10+) is the SQL-standard equivalent and is generally preferred in new schemas.

Boolean Columns

MySQL has no native boolean type. The convention is TINYINT(1), which stores 0 or 1. ORMs like Laravel and Rails treat this as a boolean automatically.

PostgreSQL has a native BOOLEAN type that accepts TRUE/FALSE, 't'/'f', 'yes'/'no', and 1/0. It's cleaner and more explicit.

JSON Support

Both databases support JSON columns, but PostgreSQL's implementation is more powerful:

If you need to query inside JSON frequently, PostgreSQL's JSONB is significantly more capable than MySQL's JSON.

CHECK Constraints

MySQL historically parsed CHECK constraints but silently ignored them. MySQL 8.0.16+ enforces them, but many MySQL installations are still on older versions or have legacy schemas that relied on the old behaviour.

PostgreSQL has always enforced CHECK constraints fully. If you're relying on them for data integrity, test that your MySQL version actually enforces them.

String Case Sensitivity

MySQL's default collation (utf8mb4_unicode_ci) is case-insensitive — WHERE name = 'Alice' matches 'alice', 'ALICE', etc. PostgreSQL is case-sensitive by default.

This is a common source of bugs when migrating: queries that worked in MySQL (case-insensitive match) silently return fewer results in PostgreSQL. Either use LOWER() explicitly, or use PostgreSQL's ILIKE for case-insensitive pattern matching.

Foreign Key Enforcement

Both InnoDB (MySQL) and PostgreSQL enforce foreign keys. However, MySQL's foreign key checks can be disabled with SET FOREIGN_KEY_CHECKS = 0, which is sometimes used during bulk imports. PostgreSQL uses SET session_replication_role = replica for the same purpose — a deliberate extra step that makes disabling constraints more explicit.

Which Should You Choose?

Whichever you choose, the schema design process is the same: model your entities and relationships first, pick appropriate data types, and validate the design visually before writing DDL.

Design your schema visually — MySQL or PostgreSQL

SQL Designer lets you model tables, relationships, and constraints visually and export a SQL script. Free, browser-based, no installation required.

Create a Free Account