Back to all posts

MERGE: ABAP Gets a New Keyword

André Schärpf

ABAP got a new top-level keyword. That almost never happens. The last time was WITH in AS ABAP 7.51, and before that, you'd have to go back far enough that some people working in ABAP today weren't around for it. At least when talking about ABAP SQL keywords (formerly Open SQL).

The keyword is MERGE. It landed in ABAP CE 2602.

What it does

MERGE is SQL standard. It exists on HANA, Oracle, SQL Server, and most other commercial databases. The idea is straightforward: take a source dataset, compare it against a target table using a join condition, and then decide what to do based on whether a match exists.

  • Match found → update or delete the target row
  • No match → insert a new row

One statement instead of the usual sequence of SELECT, then LOOP, then a mix of INSERT, UPDATE, DELETE, and MODIFY calls depending on what you find.

The syntax

MERGE INTO target
  USING source
  ON join_condition
  WHEN MATCHED THEN
    UPDATE SET ...
  WHEN NOT MATCHED THEN
    INSERT ALL.

target is a writable database object. source can be another database table, a CDS view, or an internal table (escaped with @ and requiring an alias). The join condition follows the same rules as SELECT ... FROM ... JOIN.

Both WHEN clauses are optional. You can use one or both, but if both appear, WHEN MATCHED must come first.

A concrete example

Say you maintain a list of material descriptions in a custom table, and periodically receive updated descriptions from an external system as an internal table. Materials that already exist need their descriptions updated. New materials need to be inserted.

Before MERGE, this typically looked something like:

LOOP AT lt_materials INTO DATA(ls_material).
  UPDATE ztmaterial_desc SET description = @ls_material-description
    WHERE matnr = @ls_material-matnr
      AND spras = @ls_material-spras.
  IF sy-subrc <> 0.
    INSERT ztmaterial_desc FROM @ls_material.
  ENDIF.
ENDLOOP.

That works, but every iteration hits the database. Twice, in the worst case.

With MERGE:

MERGE INTO ztmaterial_desc
  USING @lt_materials AS src
  ON ztmaterial_desc~matnr = src~matnr
    AND ztmaterial_desc~spras = src~spras
  WHEN MATCHED THEN
    UPDATE SET description = src~description
  WHEN NOT MATCHED THEN
    INSERT ALL.

One statement. The database handles the matching, the branching, and the writes internally. No round-trips per row.

Conditional updates

The WHEN MATCHED clause supports additional conditions via AND. This is useful when you don't want to blindly overwrite every matching row.

Consider a product pricing table where some prices are manually maintained and should not be overwritten by an automated feed. The source table lt_price_feed only carries product_id, price, and currency, so the INSERT spells out the columns explicitly:

MERGE INTO ztproduct_price
  USING @lt_price_feed AS feed
  ON ztproduct_price~product_id = feed~product_id
  WHEN MATCHED AND ztproduct_price~manual_price = @abap_false THEN
    UPDATE SET price = feed~price,
               currency = feed~currency
  WHEN NOT MATCHED THEN
    INSERT ( product_id, price, currency )
      VALUES ( feed~product_id, feed~price, feed~currency ).

Rows flagged as manually maintained stay untouched. Everything else gets the new price. Because the source doesn't include manual_price, INSERT ALL wouldn't work here (column counts must match between source and target). The explicit VALUES clause handles that cleanly.

DELETE on match

WHEN MATCHED can also trigger a DELETE instead of an UPDATE. If the source dataset represents items to remove from the target:

MERGE INTO ztdelivery_block
  USING @lt_resolved_blocks AS resolved
  ON ztdelivery_block~vbeln = resolved~vbeln
    AND ztdelivery_block~posnr = resolved~posnr
  WHEN MATCHED THEN DELETE.

No WHEN NOT MATCHED clause here. If a resolved block has no matching entry in the target table, nothing happens. Clean.

The weak keyword trick

Introducing a new top-level keyword in ABAP is risky because of macros. If someone defined a macro named MERGE anywhere in their codebase, the new keyword would cause syntax errors in that program.

The ABAP team solved this by making MERGE a weak keyword. Macros take precedence. If a program defines a MERGE macro, the macro wins. The program compiles fine, but the new MERGE statement is unavailable in that program. No escape mechanism, no override.

In ABAP Cloud, macros don't exist, so the conflict never arises. For on-premise systems, this only becomes relevant when MERGE ships in the next on-premise release.

What it doesn't do

SQL Server has a WHEN NOT MATCHED BY SOURCE clause that fires when a row exists in the target but not in the source. Neither ABAP nor HANA support this yet. The join is a right outer join: unmatched source rows are covered, unmatched target rows are not.

If you need to delete target rows that are absent from the source (a full sync pattern), you still need a separate DELETE statement.

When to use it

MERGE is a natural fit for sync operations. Importing master data from external systems, applying batch updates from file uploads, reconciling staging tables against production data. Anywhere you currently write a loop that checks "does this row exist?" and then branches into insert-or-update logic.

It won't replace every MODIFY. For simple cases where every row should be upserted unconditionally, MODIFY is still shorter. MERGE earns its keep when the logic branches: update only under certain conditions, delete on match, insert with transformed values.

The VALUES clause in WHEN NOT MATCHED also supports expressions and ABAP SQL functions, so you can transform data on the way in without touching an internal table first.

Further reading

The official ABAP keyword documentation covers the full syntax, including the dynamic form MERGE (dynamic_syntax) and options like %_HINTS and OPTIONS PRIVILEGED ACCESS. The executable demo class CL_DEMO_SQL_MERGE shows multiple merge operations against demo tables if you want to try it hands-on.