Contents

Data Engineering › Ingestion

Append vs Upsert (MERGE)

Adding new rows vs updating existing ones by key.

Also known as: append vs upsert, append-only vs merge, insert vs upsert loads, merge load, append load

When loading new data into a table, there are two basic write patterns.

AppendMerge (upsert)
What it doesAdds new rows only (INSERT)Inserts new rows, and updates existing ones that match a key
Needs a key?NoYes (the match condition)
HistoryKeeps every version/event as its own rowKeeps only the latest state per key
Duplicates on rerunYes, unless deduplicatedNo: reruns overwrite
CostCheap, a simple writeMore expensive: it must find matching rows
Typical forEvents, logs, clickstream, transactions, raw layersCurrent-state tables: customers, products, orders with changing status
-- Append
INSERT INTO raw_events SELECT * FROM staging_events;

-- Merge
MERGE INTO dim_customer t
USING staging_customers s ON t.customer_id = s.customer_id
WHEN MATCHED THEN UPDATE SET name = s.name, email = s.email, updated_at = s.updated_at
WHEN NOT MATCHED THEN INSERT (customer_id, name, email, updated_at)
                      VALUES (s.customer_id, s.name, s.email, s.updated_at);

(Syntax varies by database. See MERGE and upsert.)

How to choose

  • Data is a series of facts that never change (an event happened): append. History is the point, and each event is unique.
  • Data is the current state of an entity that changes: merge, to keep one row per entity. Or append every version, then derive the latest (deduplication).
  • Raw/landing layers are usually append-only, to keep everything received. Cleaned layers may merge (landing zone).
  • Need history of changes (what was this customer’s address last year)? Append versions, or use a slowly changing dimension design (SCD types).

Idempotency

A rerun of an append job duplicates data unless you guard it: delete-then-insert for that partition, or deduplicate afterwards, or record which batches have loaded. A merge is naturally rerunnable, since the same input produces the same result. Prefer one of these to a blind append when jobs may retry (incremental loads, high-water marks).

Cautions for merge

  • Duplicate keys in the source batch cause errors or arbitrary results. Deduplicate the incoming data first.
  • Out-of-order data: don’t overwrite a newer row with an older one. Add a condition (WHEN MATCHED AND s.updated_at > t.updated_at).
  • Performance: merges on very large tables can be heavy. Limit by partition or by a date range where possible.
  • Deletes aren’t handled by default. Handle them explicitly, if the source signals deletions.