ORACLE
POSTGRESQL
SQL
SQL SERVER

How to Use MERGE in SQL Query Statements: Complete Guide

intro

Let’s learn everything you need to know about using MERGE in SQL query statements to master data synchronization at the database level.

Tools used in the tutorial
Tool Description Link
Dbvisualizer DBVISUALIZER
TOP RATED DATABASE MANAGEMENT TOOL AND SQL CLIENT

Synchronization is one of the most delicate operations when dealing with two different sources. The end goal is to ensure that the target contains data merged from the source. In SQL, this typically requires conditional logic at the application level and several statements. Alternatively, you can achieve this by utilizing the MERGE statement in SQL query statements.

In this article, you will learn what the SQL MERGE statement is, how it works, and how to use it in a complete example.

Let’s dive in!

What Is the SQL MERGE Statement?

MERGE is used to perform conditional INSERT, UPDATE, or DELETE operations in a single SQL statement—something that would normally require multiple statements. At present, MERGE is only supported in PostgreSQL, SQL Server, and Oracle.

In most cases, you want to use MERGE in SQL query statements to synchronize two tables by inserting, updating, or deleting rows in one table based on differences found in the other table.

In particular, MERGE matches rows from both tables based on a specified condition. If a match is found, it can update or delete the row in the target table, while non-matching rows from the source can be inserted.

MERGE in SQL Query: Syntax and First Examples

The syntax of the SQL MERGE statement in a standard syntax follows this structure:

Copy
        
1 MERGE INTO target_table AS target 2 USING source_table AS source 3 ON target.matching_column = source.matching_column 4 [WHEN MATCHED THEN 5 UPDATE SET target.column_1 = source.column_1, target.column_2 = source.column_2, target.column_n = source.column_n | DELETE] 6 [WHEN NOT MATCHED THEN 7 INSERT (column_1, column_2, column_n) VALUES (source.column_1, source.column_2, source.column_n);]

First, MERGE INTO target_table AS target specifies target_table as the table to be modified and assigns it the alias target for easy reference. Learn more in our guide on the SQL alias mechanism.

Then, USING source_table AS source defines source_table as the data source and assigns it the alias source. The ON clause indicates the join condition that determines matches between target and source rows. Only rows meeting this condition are eligible for actions under WHEN clauses:

  • WHEN MATCHED THEN: If a match is found, the query performs an UPDATE action by setting target columns to the values in corresponding source columns. Or, alternatively, it runs a DELETE statement to remove the matching row in target.
  • WHEN NOT MATCHED THEN: If no match is found, it performs an INSERT to add a new row to target with values from source.

In simpler terms, using MERGE in SQL query statements means performing a join from source_table to the target_table, producing zero or more candidate change rows. For each candidate change row, the query determines the status of MATCHED or NOT MATCHED, based on whether the condition after ON is met or not. Next, it executes the action in the selected WHEN clause. For each candidate change row, no more than one WHEN clause is executed.

Notes:

  • To do nothing on a specific scenario, simply omit the action under WHEN MATCHED or WHEN NOT MATCHED.
  • At least one WHEN clause is required.

PostgreSQL MERGE Syntax

Copy
        
1 [ WITH with_query [, ...] ] 2 MERGE INTO [ ONLY ] target_table_name [ * ] [ [ AS ] target_alias ] 3 USING data_source ON join_condition 4 when_clause [...] 5 [ RETURNING { * | output_expression [ [ AS ] output_name ] } [, ...] ]

Where data_source is:

Copy
        
1 { [ ONLY ] source_table_name [ * ] | ( source_query ) } [ [ AS ] source_alias ]

And when_clause is:

Copy
        
1 { WHEN MATCHED [ AND condition ] THEN { merge_update | merge_delete | DO NOTHING } | 2 WHEN NOT MATCHED BY SOURCE [ AND condition ] THEN { merge_update | merge_delete | DO NOTHING } | 3 WHEN NOT MATCHED [ BY TARGET ] [ AND condition ] THEN { merge_insert | DO NOTHING } }

And merge_insert is:

Copy
        
1 INSERT [( column_name [, ...] )] 2 [ OVERRIDING { SYSTEM | USER } VALUE ] 3 { VALUES ( { expression | DEFAULT } [, ...] ) | DEFAULT VALUES }

And merge_update is:

Copy
        
1 UPDATE SET { column_name = { expression | DEFAULT } | 2 ( column_name [, ...] ) = [ ROW ] ( { expression | DEFAULT } [, ...] ) | 3 ( column_name [, ...] ) = ( sub-SELECT ) 4 } [, ...]

And merge_delete is:

Copy
        
1 DELETE

Specific considerations:

  • Support for an optional WITH clause to define a PostgreSQL CTE (Common Table Expression) that can be referenced within the scope of the statement.
  • DO NOTHING syntax to skip specific rows in the source table.
  • An optional RETURNING clause to compute and return value(s) based on each row inserted, updated, or deleted.
  • MERGE is not supported if the target table is a materialized view.
  • WHEN NOT MATCHED BY SOURCE is an addition to the SQL standard to address rows in the target table that do not match rows in the data_source. This is only available in PostgreSQL 17+.
  • Additional conditions that return a boolean are supported in WHEN clauses.
  • No WHERE clauses are supported in UPDATE, INSERT, and DELETE commands.

SQL Server MERGE Syntax

Check out the documentation for the SQL Server MERGE syntax.

Specific considerations:

  • Support for, additional conditions, WHEN NOT MATCHED, and a CTE just like in PostgreSQL.
  • TOP can be used to specify the number or percentage of affected rows. For example, if TOP (10) is specified, 6 might be updated and 4 inserted.

Oracle MERGE Syntax

See the documentation for the Oracle MERGE syntax.

Specific considerations:

  • Follows the syntax from the SQL standards, with the main difference that DELETE statements are specified with a special DELETE WHERE clause after UPDATE statements.
  • The DELETE WHERE clause cleans up data in a table while updating it.
  • Support for WHERE clauses in INSERT and UPDATE statements

Complete Example of a MERGE Query in SQL

Now, we will see how to use MERGE in SQL query real-world scenarios.

Note: The queries below will be run in PostgreSQL, but you can easily adapt them to SQL Server, Oracle, or any other DBMS supporting MERGE. Also, the queries will be executed in DbVisualizer, a powerful PostgreSQL database client. Any other client will do.

Suppose that you have the employees and archive_employees tables in your company PostgreSQL database. employees contains partial data and is the target table, while cms_employees is the source table. cms_employees was imported into the current database from the CMS and contains updated data.

This is the data contained in employees:

The Data tab in DbVisualizer for employees
The Data tab in DbVisualizer for employees

The Data tab in DbVisualizer for employees

And this is the data contained in cms_employees:

The Data tab in DbVisualizer for cms_employees
The Data tab in DbVisualizer for cms_employees

Note that the two tables have a different structure.

Your goal is to update the employees table with the records from new_employees, inserting any new employees that are missing, and updating their salary and position based on the information in cms_employees.

Here is how you can do it with a MERGE query:

Copy
        
1 MERGE INTO employees AS e 2 USING cms_employees AS ce 3 ON e.full_name = CONCAT(ce.name, ' ', ce.surname) 4 WHEN MATCHED THEN 5 UPDATE SET position = ce.position, salary = ce.salary 6 WHEN NOT MATCHED THEN 7 INSERT (full_name, position, salary) 8 VALUES (CONCAT(ce.name, ' ', ce.surname), ce.position, ce.salary);

The above query synchronizes data between the employees and cms_employees tables. It matches records based on the concatenation of the name and surname from cms_employees to the full_name in employees. When a match is found, it updates the position and salary in employees. If no match is found, it inserts a new record into employees using the name, surname, position, and salary from cms_employees.

Run the query:

Executing the MERGE query in DbVisualizer
Executing the MERGE query in DbVisualizer

The query will affect 5 rows, updating 4 and adding 1. Verify that by taking a look at employees:

Note the marged data
Note the marged data

Note that all positions and salary were updated as desired from cms_employees to employees. Also, the “Maria Williams” record was added, as this did not appear in employees.

Now, assume that you also want to delete all employees records that are not present in cms_employees. Below is the query you need to write:

Copy
        
1 MERGE INTO employees AS e 2 USING cms_employees AS ce 3 ON e.full_name = CONCAT(ce.name, ' ', ce.surname) 4 WHEN MATCHED THEN 5 UPDATE SET position = ce.position, salary = ce.salary 6 WHEN NOT MATCHED THEN 7 INSERT (full_name, position, salary) 8 VALUES (CONCAT(ce.name, ' ', ce.surname), ce.position, ce.salary) 9 WHEN NOT MATCHED BY SOURCE THEN 10 DELETE;

This time, the result will be as follows:

Note that the data was merged differently compared to before
Note that the data was merged differently compared to before

The “Bob Johnson” record was removed as expected.

Et voilà! You are now a master of using MERGE in SQL query statements.

Conclusion

In this guide, you learned what the SQL MERGE statement is and how to use it in PostgreSQL, SQL Server, and Oracle. You now know that it enables you to perform conditional INSERT, UPDATE, or DELETE operations with a single SQL statement.

As shown here, DbVisualizer simplifies managing MERGE queries. This powerful database client supports multiple DBMS technologies and offers advanced features like visual data exploration, query optimization, SQL formatting, and ERD-like schema generation. Try DbVisualizer for free today!

FAQ

Is the MERGE statement part of the SQL standard?

Yes, the MERGE statement is part of the SQL ISO/ANSI standard. It was introduced in SQL:2003 and extended even further in SQL:2008. As of this writing, the MERGE statement is supported by PostgreSQL, SQL Server, and Oracle.

How to merge two queries in SQL?

To merge two queries in SQL, you can use the SQL UNION and SQL UNION ALL operators. UNION combines results from both queries, removing duplicate rows, while UNION ALL includes all rows, keeping duplicates. Both queries must have the same number of columns and compatible data types in corresponding columns.

How does the SQL Server MERGE statement work?

The SQL Server MERGE statement works like that of PostgreSQL and Oracle, allowing conditional updates, insertions, or deletions in a target table based on data from a source table. It compares rows in both tables using a specified key and performs actions such as UPDATE, INSERT, or DELETE depending on the matches found.

How to use MERGE in MySQL?

MySQL does not directly support the SQL MERGE statement. For similar results on insertions, you can use the INSERT ... ON DUPLICATE KEY UPDATE syntax to perform upsert operations.

How to use CTEs with MERGE in SQL query statements?

You can use a CTE (Common Table Expression) with a MERGE statement in PostgreSQL and SQL Server by defining the CTE before the MERGE. The CTE can provide a subset of data or transformed data to be used in the MERGE query for matching, updating, or inserting rows.

Dbvis download link img
About the author
Antonello Zanini

Antonello is a software engineer, and often refers to himself as a technology bishop. His mission is to spread knowledge through writing.

The Table Icon
Sign up to receive The Table's roundup
More from the table
Title Author Tags Length Published
title

PostgreSQL Truncate Table Reset Sequence Guide

author Antonello Zanini tags POSTGRESQL 6 min 2025-02-17
title

Clustered vs Non-Clustered Index: Complete SQL Guide

author Antonello Zanini tags MySQL ORACLE POSTGRESQL SQL SQL SERVER 8 min 2025-02-13
title

Queries and Subqueries in SQL: The Definitive Guide

author Lukas Vileikis tags DbVisualizer MySQL POSTGRESQL SQL SQL SERVER 7 min 2025-02-12
title

How to Optimize a Clustered Index Scan in SQL

author Antonello Zanini tags MySQL SQL SQL SERVER 7 min 2025-02-11
title

SQL Derived Table: Everything You Need to Know

author Antonello Zanini tags MySQL POSTGRESQL SQL SQL SERVER 9 min 2025-02-06
title

How To List Databases in PostgreSQL: A Complete Guide

author Antonello Zanini tags POSTGRESQL 8 min 2025-02-05
title

Understanding MVCC in MySQL

author Lukas Vileikis tags DbVisualizer MySQL SQL 7 min 2025-02-05
title

Postgres Create Array of Numbers: Complete Guide

author Lukas Vileikis tags POSTGRESQL SQL 7 min 2025-02-03
title

SQL Injection Cheat Sheet: SQLi 101

author Lukas Vileikis tags MySQL SECURITY SQL 10 min 2025-01-29
title

SQL PARTITION BY in PostgreSQL: A Guide to Window Functions and Data Segmentation

author Leslie S. Gyamfi tags SQL 7 min 2025-01-28

The content provided on dbvis.com/thetable, including but not limited to code and examples, is intended for educational and informational purposes only. We do not make any warranties or representations of any kind. Read more here.