A complete, step-by-step guide for migrating your database from MySQL to PostgreSQL โ from schema conversion to data migration to production cutover.
Paste your MySQL CREATE TABLE statements and get PostgreSQL-ready SQL in seconds.
Translate MySQL โ PostgreSQL Diff Two SchemasTeams typically migrate from MySQL to PostgreSQL for these reasons:
Use mysqldump to export just the schema (no data) so you can review and translate it:
mysqldump --no-data --routines --events \
-u username -p database_name > mysql_schema.sql
--routines and --events to capture stored procedures and events. You'll need to manually translate these since automated tools typically only handle CREATE TABLE.
Review the exported file for MySQL-specific syntax:
ENGINE=InnoDB โ PostgreSQL doesn't use storage enginesAUTO_INCREMENT โ Will become SERIAL or GENERATED ALWAYS AS IDENTITYBACKTICK` quoting โ PostgreSQL uses double quotesENUM and SET types โ Need special handlingThis is where most migrations get stuck. MySQL and PostgreSQL have different:
You have three options:
Use the SQL Dialect Translator to convert your mysqldump output to PostgreSQL syntax. It handles type mapping, auto-increment conversion, and identifier quoting.
If you're redesigning the schema as part of migration, paste your MySQL schema and your target PostgreSQL schema into SchemaLens. It will show you exactly what's different and generate the migration SQL.
For small schemas, you can manually rewrite the CREATE TABLE statements. Use the type mapping table below as a reference.
The SQL Dialect Translator converts MySQL schemas to PostgreSQL in seconds โ including type mapping, auto-increment syntax, and constraint adaptation.
Try the Translator โAfter translating the schema, create the tables in PostgreSQL and migrate the data.
pgloader is the gold standard for MySQL โ PostgreSQL data migration. It handles type casting, index creation, and parallel loading.
pgloader mysql://user:pass@host/dbname postgresql://user:pass@host/dbname
For complex transformations, use Python with SQLAlchemy and pandas:
import pandas as pd
from sqlalchemy import create_engine
mysql = create_engine('mysql+pymysql://user:pass@host/db')
postgres = create_engine('postgresql://user:pass@host/db')
df = pd.read_sql('SELECT * FROM users', mysql)
df.to_sql('users', postgres, if_exists='append', index=False)
For small datasets, export to CSV and use PostgreSQL's COPY command:
-- MySQL
SELECT * INTO OUTFILE '/tmp/users.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
FROM users;
-- PostgreSQL
COPY users FROM '/tmp/users.csv' CSV HEADER;
utf8mb4 maps to PostgreSQL's UTF8, but emoji and 4-byte characters can cause issues if not handled properly. Verify character set compatibility before migration.
Never skip validation. Here's a systematic approach:
SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'dbname';Export both schemas and diff them to catch anything you missed:
-- MySQL
mysqldump --no-data -u user -p mysql_db > mysql_schema.sql
-- PostgreSQL
pg_dump --schema-only -U user postgres_db > postgres_schema.sql
Then paste both into SchemaLens to see a visual diff of any remaining differences.
SchemaLens compares your MySQL and PostgreSQL schemas side-by-side and highlights any tables, columns, or indexes that didn't migrate correctly.
Compare Schemas Free โ| MySQL Type | PostgreSQL Type | Notes |
|---|---|---|
INT AUTO_INCREMENT | SERIAL | Use BIGSERIAL for BIGINT |
TINYINT(1) | BOOLEAN | MySQL often uses this for booleans |
VARCHAR(n) | VARCHAR(n) | No change needed |
TEXT | TEXT | No change needed |
LONGTEXT | TEXT | PostgreSQL TEXT is unlimited |
DATETIME | TIMESTAMP | Or TIMESTAMPTZ with timezone |
JSON | JSONB | JSONB is faster and indexable |
ENUM | VARCHAR + CHECK | PostgreSQL has no native ENUM in CREATE TABLE |
SET | TEXT or array | No direct equivalent |
BLOB | BYTEA | Binary data storage |
FLOAT | REAL | Or DOUBLE PRECISION |
DOUBLE | DOUBLE PRECISION | No change needed |
DECIMAL(p,s) | NUMERIC(p,s) | No change needed |
BIT | BOOLEAN | Or BIT if you need bit fields |
VARCHAR behaves similarly but not identically. PostgreSQL is stricter about trailing spaces.UserName becomes username unless quoted.SELECT 1/4 returns 0 in MySQL but 0.25 in PostgreSQL (integer vs numeric division).MATCH ... AGAINST doesn't exist in PostgreSQL. Use tsvector and tsquery instead.CONCAT() works in both, but || is the standard in PostgreSQL.For schemas under 50 tables with a few GB of data, plan for 4-8 hours of focused work. Large schemas (500+ tables) or TB-scale data can take days or weeks. The schema translation is usually the fastest part; data migration and testing take the most time.
Yes, using a dual-write pattern: write to both MySQL and PostgreSQL during a transition period, then switch reads to PostgreSQL once validated. Tools like Debezium can help with CDC (Change Data Capture).
These must be manually rewritten. MySQL uses SQL/PSM syntax while PostgreSQL uses PL/pgSQL. The logic is often similar but function names and conventions differ.
Yes, pgloader is open-source and free. It's the most popular tool for MySQL โ PostgreSQL data migration and handles most edge cases automatically.
Probably. Even with ORMs like Prisma or SQLAlchemy, you may need to update connection strings, query syntax, and handling of specific data types. Test thoroughly in a staging environment.
Convert your MySQL CREATE TABLE statements to PostgreSQL syntax in seconds, then diff the result to catch anything the translator missed.
Translate Schema โ Diff & Validate โ