MySQL AUTO_INCREMENT → PostgreSQL Migration
Converting AUTO_INCREMENT columns to PostgreSQL identity columns, and why the underlying sequence needs an explicit sync step after loading data.
MEDIUM
Quick answer
Replace AUTO_INCREMENT with GENERATED ALWAYS AS IDENTITY. This handles new rows correctly, but if you load existing rows with their original IDs, the sequence itself will still start from 1 and must be synced to the actual max ID or the next insert will collide.
Why it breaks
MySQL's AUTO_INCREMENT is a column attribute that both generates new values and tracks the current counter together. PostgreSQL splits this into two objects: an identity column (or a plain column backed by a sequence) and the sequence object itself, which does not know about rows that were inserted with an explicit ID during data load — only rows inserted through the identity column advance it automatically.
Real examples
Column definition
CREATE TABLE orders (
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id)
); CREATE TABLE orders (
id INTEGER GENERATED ALWAYS AS IDENTITY,
PRIMARY KEY (id)
); The part most guides skip: syncing the sequence
-- After bulk-loading 10,000 existing orders with explicit ids 1..10000 SELECT setval(pg_get_serial_sequence('orders', 'id'), (SELECT MAX(id) FROM orders));
-- Without this, the next INSERT without an explicit id starts at 1 again and collides with an existing row. Safe migration options
- Use GENERATED ALWAYS AS IDENTITY for new schemas — it is the modern, SQL-standard PostgreSQL syntax.
- GENERATED BY DEFAULT AS IDENTITY (or the older SERIAL) if your load process needs to insert explicit ID values during migration itself, since ALWAYS rejects explicit values unless OVERRIDING SYSTEM VALUE is used.
- After loading data, always discover the sequence name with pg_get_serial_sequence() rather than guessing it — never hardcode a sequence name.
PostgreSQL solution
CREATE TABLE ... (id BIGINT GENERATED ALWAYS AS IDENTITY, ...). The identity clause replaces AUTO_INCREMENT directly in the column definition.
Validation
Before cutover
After loading data, run: SELECT setval(pg_get_serial_sequence('table_name', 'id'), (SELECT MAX(id) FROM table_name)); — verify with SELECT nextval(pg_get_serial_sequence('table_name','id')) that it returns a value greater than any existing id before allowing new application inserts.
Scan your own schema
This page documents detector rule AUTO_INCREMENT in the DevEquiv analyzer — paste or
upload your real MySQL/MariaDB schema to find every occurrence of this (and 19 other
documented issues) automatically, with a prioritized readiness report.
References
- MySQL Manual — AUTO_INCREMENT (v8.0) — checked 2026-07-30
- PostgreSQL Manual — Identity Columns (v17) — checked 2026-07-30
Last verified 2026-07-30.