DatabaseStatusSyntax FamilyMin Version
PostgreSQL full insert-on-conflict 9.5
MySQL full insert-on-duplicate-key -
MariaDB full insert-on-duplicate-key -
Oracle full merge -
SQL Server full merge -
SQLite full insert-on-conflict 3.24.0
Redshift full merge -
DB2 full merge -
Snowflake full merge -
BigQuery full merge -

Compare Databases

Database Details

PostgreSQL full
INSERT INTO users (id, email)
VALUES (1, 'a@example.com')
ON CONFLICT (id) DO UPDATE
SET email = EXCLUDED.email;
  • Conflict target must be explicitly specified.
  • Supports DO NOTHING as an alternative to DO UPDATE.
  • Also supports the SQL-standard MERGE syntax as of PostgreSQL 15.
MySQL full
INSERT INTO users (id, email)
VALUES (1, 'a@example.com')
ON DUPLICATE KEY UPDATE email = VALUES(email);
  • Triggered by any unique key violation, not just the primary key.
  • VALUES() in UPDATE clause is deprecated in 8.0.20+; use alias syntax instead.
  • No support for conditional conflict targets.
MariaDB full
INSERT INTO users (id, email)
VALUES (1, 'a@example.com')
ON DUPLICATE KEY UPDATE email = VALUES(email);
  • Same syntax as MySQL ON DUPLICATE KEY UPDATE.
  • Also supports REPLACE INTO as a delete-then-insert alternative.
Oracle full
MERGE INTO users t
USING (SELECT 1 AS id, 'a@example.com' AS email FROM dual) s
ON (t.id = s.id)
WHEN MATCHED THEN UPDATE SET t.email = s.email
WHEN NOT MATCHED THEN INSERT (id, email) VALUES (s.id, s.email);
  • Uses SQL-standard MERGE syntax.
  • Supports WHEN MATCHED and WHEN NOT MATCHED clauses with conditions.
Docs: MERGE
SQL Server full
MERGE INTO users AS t
USING (VALUES (1, 'a@example.com')) AS s(id, email)
ON t.id = s.id
WHEN MATCHED THEN UPDATE SET t.email = s.email
WHEN NOT MATCHED THEN INSERT (id, email) VALUES (s.id, s.email);
  • Uses MERGE syntax; statement must end with semicolon.
  • Supports OUTPUT clause for RETURNING-like behavior.
  • MERGE has had concurrency bugs in older versions; check KB articles.
Docs: MERGE
SQLite full
INSERT INTO users (id, email)
VALUES (1, 'a@example.com')
ON CONFLICT(id) DO UPDATE SET email = excluded.email;
  • Syntax similar to PostgreSQL but uses lowercase 'excluded' table reference.
  • Also supports INSERT OR REPLACE, but that deletes and re-inserts.
Docs: UPSERT
Redshift full
MERGE INTO users AS t
USING (VALUES (1, 'a@example.com')) AS s(id, email)
ON t.id = s.id
WHEN MATCHED THEN UPDATE SET t.email = s.email
WHEN NOT MATCHED THEN INSERT (id, email) VALUES (s.id, s.email);
  • MERGE statement added in April 2023.
  • Supports WHEN MATCHED (UPDATE or DELETE) and WHEN NOT MATCHED (INSERT) clauses.
  • No INSERT ... ON CONFLICT syntax; MERGE is the native upsert mechanism.
Docs: MERGE
DB2 full
MERGE INTO users AS t
USING (VALUES (1, 'a@example.com')) AS s(id, email)
ON t.id = s.id
WHEN MATCHED THEN UPDATE SET t.email = s.email
WHEN NOT MATCHED THEN INSERT (id, email) VALUES (s.id, s.email);
  • Supports SQL-standard MERGE.
  • Also supports multiple WHEN MATCHED clauses with conditions.
Docs: MERGE
Snowflake full
MERGE INTO users AS t
USING (SELECT 1 AS id, 'a@example.com' AS email) AS s
ON t.id = s.id
WHEN MATCHED THEN UPDATE SET t.email = s.email
WHEN NOT MATCHED THEN INSERT (id, email) VALUES (s.id, s.email);
  • Uses MERGE syntax.
  • Supports multiple WHEN clauses.
Docs: MERGE
BigQuery full
MERGE INTO users AS t
USING (SELECT 1 AS id, 'a@example.com' AS email) AS s
ON t.id = s.id
WHEN MATCHED THEN UPDATE SET t.email = s.email
WHEN NOT MATCHED THEN INSERT (id, email) VALUES (s.id, s.email);
  • Uses MERGE syntax.
  • Source and target must be different tables.
Docs: MERGE