All posts · SQL & PL/SQL

Oracle MERGE Statement: Upsert Patterns and Best Practices

The MERGE statement combines INSERT, UPDATE, and DELETE into a single atomic operation — the most efficient way to synchronise data between tables.

Anurag Jangra · March 4, 2026 · 6 min read · ... views

What MERGE does

MERGE synchronises a target table with a source dataset in one pass:

  • If a matching row exists in target: UPDATE it (or DELETE it)
  • If no matching row exists in target: INSERT it

One MERGE statement replaces the common “check if exists, then insert or update” pattern that requires multiple round trips.

Basic syntax

MERGE INTO orders tgt
USING staging_orders src
ON (tgt.order_ref = src.order_ref)
WHEN MATCHED THEN
  UPDATE SET
    tgt.status     = src.status,
    tgt.updated_dt = SYSDATE
  WHERE tgt.status != src.status  -- update only when changed
WHEN NOT MATCHED THEN
  INSERT (order_id, order_ref, customer_id, amount, status, created_dt)
  VALUES (seq_order.NEXTVAL, src.order_ref, src.customer_id, src.amount, src.status, SYSDATE);

Adding DELETE to MERGE

Delete records in the target that should no longer exist:

WHEN MATCHED THEN
  UPDATE SET tgt.status = src.status
  DELETE WHERE src.active_flag = 'N';

The DELETE clause runs after the UPDATE — it deletes the updated row if the WHERE condition is true.

MERGE with subquery source

The source can be any query, not just a table:

MERGE INTO employee_summary tgt
USING (
  SELECT department_id, COUNT(*) AS headcount, AVG(salary) AS avg_sal
  FROM employees GROUP BY department_id
) src
ON (tgt.department_id = src.department_id)
WHEN MATCHED THEN
  UPDATE SET tgt.headcount = src.headcount, tgt.avg_salary = src.avg_sal
WHEN NOT MATCHED THEN
  INSERT (department_id, headcount, avg_salary)
  VALUES (src.department_id, src.headcount, src.avg_sal);

Tracking MERGE results

MERGE INTO orders tgt USING staging src ON ...
...;

DBMS_OUTPUT.PUT_LINE('Rows merged: ' || SQL%ROWCOUNT);

SQL%ROWCOUNT after MERGE returns the total number of rows processed (inserts + updates + deletes).

MERGE in FBDI and OIC integrations

MERGE is ideal for staging table patterns in FBDI automation:

  1. Load raw data into a staging table
  2. MERGE staging into the clean target table (handle duplicates, updates, new records in one step)
  3. Clear staging after successful MERGE

This avoids separate DELETE/INSERT cycles that create unnecessary undo/redo.

Performance: avoid MERGE on indexed columns in WHERE

If the MATCHED UPDATE has a WHERE clause that references indexed columns, Oracle may not use those indexes efficiently. Test execution plans on large datasets before deploying MERGE statements in batch jobs.

Think Beyond the Implementation

Questions worth sitting with after reading this

01

Why is this architecture appropriate for this specific context — and where would it be the wrong choice?

02

What assumptions did we make that aren't stated explicitly? What happens if those assumptions are wrong?

03

What would break first if the requirements changed — volume doubled, a third system was added, or the deadline halved?

04

What alternatives did we reject, and why? Was the decision made on evidence — or habit?

AJ
Anurag Jangra
Oracle Cloud PaaS Consultant · OIC & VBCS Specialist

4.5+ years delivering enterprise Oracle Cloud integrations and VBCS applications across manufacturing, IT services, and financial sectors. OCI Certified — writes about real-world OIC, VBCS, SQL, and BI Publisher patterns from production experience.

Chat on WhatsApp