PostgreSQLMySQLSQL ServerSQLite

Duplicate key / unique constraint violation

You inserted or updated a row into a value that already exists in a primary key or unique column. The fix depends on whether the duplicate is a bug or an expected case you need to handle.

  • SQLite: UNIQUE constraint failed: customers.customer_id
  • PostgreSQL: ERROR: duplicate key value violates unique constraint "customers_pkey" DETAIL: Key (customer_id)=(1) already exists.
  • MySQL: #1062 - Duplicate entry '1' for key 'customers.PRIMARY'
  • SQL Server: Violation of PRIMARY KEY constraint 'PK_customers'. Cannot insert duplicate key in object 'dbo.customers'. The duplicate key value is (1).

Customer 1 already exists — inserting it again fails, on every engine including the one running this example live:

SQL playground
Loading editor…
⌘/Ctrl + Enter

The cause

A PRIMARY KEY or UNIQUE constraint exists specifically to make this error possible — it's the database refusing to store two rows with the same value in a column (or column set) you told it must be unique. The error is doing its job. The question is why your application produced a duplicate in the first place.

Figure out which situation you're in

It's a genuine bug — you generated the wrong ID, or ran the same insert twice (a retried request, a re-run migration). Fix the thing generating the duplicate; the constraint just caught it before it corrupted data.

The duplicate is expected and you need "insert, or update if it's already there." That's not a bug to fix — it's the wrong statement for the job. Use an upsert (a scratch table here, so the shared customers data above stays untouched for the rest of this page):

SQL playground
Loading editor…
⌘/Ctrl + Enter

See UPSERT for the MySQL (ON DUPLICATE KEY UPDATE) and SQL Server (MERGE) equivalents — the syntax differs more here than almost anywhere else in SQL.

You want to silently skip duplicates instead of updating them — DO NOTHING in place of DO UPDATE SET ... leaves the existing row exactly as it was, with no error:

INSERT INTO scratch_customers (customer_id, name)
VALUES (1, 'Ignored')
ON CONFLICT (customer_id) DO NOTHING;

Before you write the upsert, check the constraint is the right one

A duplicate key error on a column you didn't expect to be constrained usually means the unique constraint is broader than you think — e.g. a composite unique constraint across (customer_id, order_date) will reject a second order on the same day even though order_id itself is fine. Check what's actually declared unique before assuming the fix is "just handle the duplicate":

-- PostgreSQL / MySQL
SELECT constraint_name, column_name
FROM information_schema.key_column_usage
WHERE table_name = 'customers';
  • UPSERT — insert-or-update syntax across PostgreSQL, MySQL, SQL Server, and SQLite.
  • INSERT — multi-row inserts and the basics this builds on.
  • Deduplicate rows — cleaning up duplicates that already made it into a table without a constraint stopping them.
Try the plain duplicate insert above, then the ON CONFLICT version, and compare what each does to the row count.Open the playground →