Available in versions: Dev (3.20) | Latest (3.19) | 3.18 | 3.17 | 3.16 | 3.15 | 3.14 | 3.13 | 3.12 | 3.11 | 3.10
The MERGE statement
Applies to ✅ Open Source Edition ✅ Express Edition ✅ Professional Edition ✅ Enterprise Edition
The MERGE statement is one of the most advanced standardised SQL constructs. While it can be used for UPSERT
semantics (similar to INSERT .. ON DUPLICATE KEY UPDATE or INSERT .. ON CONFLICT), it is much more than that.
The point of the standard MERGE
statement is to take a TARGET
table, and merge (INSERT
, UPDATE
, or DELETE
) data from a SOURCE
table into it. Here is an example:
-- Check if there is already an author called 'Hitchcock' -- If there is, rename him to John. If there isn't add him. MERGE INTO AUTHOR USING (SELECT 1 FROM DUAL) ON (LAST_NAME = 'Hitchcock') WHEN MATCHED THEN UPDATE SET FIRST_NAME = 'John' WHEN NOT MATCHED THEN INSERT (LAST_NAME) VALUES ('Hitchcock');
create.mergeInto(AUTHOR) .using(create.selectOne()) .on(AUTHOR.LAST_NAME.eq("Hitchcock")) .whenMatchedThenUpdate() .set(AUTHOR.FIRST_NAME, "John") .whenNotMatchedThenInsert(AUTHOR.LAST_NAME) .values("Hitchcock") .execute();
Think of the MERGE
statement as a RIGHT JOIN between TARGET
and SOURCE
tables, where the ON
clause defines whether there is a MATCH
between TARGET
and SOURCE
rows:
- If there's a
MATCH
, anUPDATE
orDELETE
operation on theTARGET
row is possible - If there's no
MATCH
, then theSOURCE
row can be used to perform anINSERT
operation on theTARGET
table
Multiple WHEN clauses
Various dialects support the standard SQL syntax for having multiple WHEN
clauses, where an additional predicates are supplied with each clause.
MERGE INTO AUTHOR USING (SELECT 1 FROM DUAL) ON (LAST_NAME = 'Hitchcock') WHEN MATCHED AND FIRST_NAME = 'Alfred' THEN UPDATE SET FIRST_NAME = 'John' WHEN MATCHED AND FIRST_NAME = 'Diane' THEN UPDATE SET FIRST_NAME = 'Jane' WHEN NOT MATCHED THEN INSERT (LAST_NAME) VALUES ('Hitchcock');
create.mergeInto(AUTHOR) .using(create.selectOne()) .on(AUTHOR.LAST_NAME.eq("Hitchcock")) .whenMatchedAnd(AUTHOR.FIRST_NAME.eq("Alfred")) .thenUpdate().set(AUTHOR.FIRST_NAME, "John") .whenMatchedAnd(AUTHOR.FIRST_NAME.eq("Diane")) .thenUpdate().set(AUTHOR.FIRST_NAME, "Jane") .whenNotMatchedThenInsert(AUTHOR.LAST_NAME) .values("Hitchcock") .execute();
If the above isn't supported in a dialect, it can be emulated by collapsing the various WHEN
clauses into a single one that uses a CASE expression to cover all the conditions and results.
Dialect support
This example using jOOQ:
mergeInto(AUTHOR).using(selectOne()).on(AUTHOR.LAST_NAME.eq("Hitchcock")).whenMatchedThenUpdate().set(AUTHOR.FIRST_NAME, "John").whenNotMatchedThenInsert(AUTHOR.LAST_NAME).values("Hitchcock")
Translates to the following dialect specific expressions:
DB2
MERGE INTO AUTHOR USING ( SELECT 1 one FROM SYSIBM.DUAL ) ON AUTHOR.LAST_NAME = 'Hitchcock' WHEN MATCHED THEN UPDATE SET AUTHOR.FIRST_NAME = 'John' WHEN NOT MATCHED THEN INSERT (LAST_NAME) VALUES ('Hitchcock')
Derby
MERGE INTO AUTHOR USING ( SELECT 1 one FROM SYSIBM.SYSDUMMY1 ) ON AUTHOR.LAST_NAME = 'Hitchcock' WHEN MATCHED THEN UPDATE SET AUTHOR.FIRST_NAME = 'John' WHEN NOT MATCHED THEN INSERT (LAST_NAME) VALUES ('Hitchcock')
Exasol, H2
MERGE INTO AUTHOR USING ( SELECT 1 one ) ON AUTHOR.LAST_NAME = 'Hitchcock' WHEN MATCHED THEN UPDATE SET AUTHOR.FIRST_NAME = 'John' WHEN NOT MATCHED THEN INSERT (LAST_NAME) VALUES ('Hitchcock')
Firebird
MERGE INTO AUTHOR USING ( SELECT 1 one FROM RDB$DATABASE ) ON AUTHOR.LAST_NAME = 'Hitchcock' WHEN MATCHED THEN UPDATE SET AUTHOR.FIRST_NAME = 'John' WHEN NOT MATCHED THEN INSERT (LAST_NAME) VALUES ('Hitchcock')
Hana
MERGE INTO AUTHOR USING ( SELECT 1 one FROM SYS.DUMMY ) ON AUTHOR.LAST_NAME = 'Hitchcock' WHEN MATCHED THEN UPDATE SET AUTHOR.FIRST_NAME = 'John' WHEN NOT MATCHED THEN INSERT (LAST_NAME) VALUES ('Hitchcock')
HSQLDB
MERGE INTO AUTHOR USING ( SELECT 1 one FROM (VALUES (1)) AS dual (dual) ) ON AUTHOR.LAST_NAME = 'Hitchcock' WHEN MATCHED THEN UPDATE SET AUTHOR.FIRST_NAME = 'John' WHEN NOT MATCHED THEN INSERT (LAST_NAME) VALUES ('Hitchcock')
Oracle
MERGE INTO AUTHOR USING ( SELECT 1 one ) ON (AUTHOR.LAST_NAME = 'Hitchcock') WHEN MATCHED THEN UPDATE SET AUTHOR.FIRST_NAME = 'John' WHEN NOT MATCHED THEN INSERT (LAST_NAME) VALUES ('Hitchcock')
Postgres, Vertica
MERGE INTO AUTHOR USING ( SELECT 1 one ) AS dummy_30260683("one") ON AUTHOR.LAST_NAME = 'Hitchcock' WHEN MATCHED THEN UPDATE SET FIRST_NAME = 'John' WHEN NOT MATCHED THEN INSERT (LAST_NAME) VALUES ('Hitchcock')
Snowflake
MERGE INTO AUTHOR USING ( SELECT 1 one ) ON AUTHOR.LAST_NAME = 'Hitchcock' WHEN MATCHED THEN UPDATE SET FIRST_NAME = 'John' WHEN NOT MATCHED THEN INSERT (LAST_NAME) VALUES ('Hitchcock')
SQLServer
MERGE INTO AUTHOR USING ( SELECT 1 one ) AS dummy_30260683([one]) ON AUTHOR.LAST_NAME = 'Hitchcock' WHEN MATCHED THEN UPDATE SET AUTHOR.FIRST_NAME = 'John' WHEN NOT MATCHED THEN INSERT (LAST_NAME) VALUES ('Hitchcock');
Sybase
MERGE INTO AUTHOR USING ( SELECT 1 one FROM SYS.DUMMY ) AS dummy_30260683([one]) ON AUTHOR.LAST_NAME = 'Hitchcock' WHEN MATCHED THEN UPDATE SET AUTHOR.FIRST_NAME = 'John' WHEN NOT MATCHED THEN INSERT (LAST_NAME) VALUES ('Hitchcock')
Teradata
MERGE INTO AUTHOR USING ( SELECT 1 one FROM ( SELECT 1 AS "dual" ) AS "dual" ) ON AUTHOR.LAST_NAME = 'Hitchcock' WHEN MATCHED THEN UPDATE SET FIRST_NAME = 'John' WHEN NOT MATCHED THEN INSERT (LAST_NAME) VALUES ('Hitchcock')
ASE, Access, Aurora MySQL, Aurora Postgres, BigQuery, ClickHouse, CockroachDB, DuckDB, Informix, MariaDB, MemSQL, MySQL, Redshift, SQLDataWarehouse, SQLite, Trino, YugabyteDB
/* UNSUPPORTED */
Generated with jOOQ 3.20. Translate your own SQL on our website
Feedback
Do you have any feedback about this page? We'd love to hear it!