← All cheatsheets
Data Engineer · #075 · October 16, 2026 · 2 min read

Incremental load: stop reloading two years to add one day

Full load versus incremental load, the watermark pattern with an upsert and a two-day overlap, and the three things an incremental pipeline misses: deletes, late data and a missing updated_at.

Get the free PDF

One page, print-ready, free to share. No signup needed.

Download the PDF

A nightly job that truncates a table and reloads two years of history to add one day is simple and correct. It is also slow, and it gets slower every month. This page covers full load versus incremental, the watermark pattern, the upsert, and the late data that arrives after your watermark has moved on. The print-ready A4 PDF is at the bottom.

Full load

  • TRUNCATE plus INSERT: simple, correct, slow.
  • Every night it reads everything to add one day.

Incremental

  • WHERE updated_at > :mark pulls only what changed.
  • The watermark is the max value you loaded last time. Store it.
  • An upsert writes updates too, not only inserts.

The watermark pattern

-- 1. where did we stop?
SELECT MAX(updated_at) FROM dw.orders;   -- :mark

-- 2. only what changed since, with a 2-day overlap
INSERT INTO dw.orders
SELECT * FROM src.orders
 WHERE updated_at > :mark - INTERVAL '2 days'
ON CONFLICT (order_id) DO UPDATE
   SET status = EXCLUDED.status,
       updated_at = EXCLUDED.updated_at;

Read the max you loaded last time, pull everything above it, write it with an upsert, store the new max.

The overlap costs two days of rows and buys you the late ones. The upsert makes the overlap harmless: a row loaded twice is updated, not duplicated. The ON CONFLICT needs a unique key on order_id in the target table.

Remember

  • Late data lands after the watermark. This is the one to plan for.
  • Overlap: reload the last two days and dedupe.
  • A full load is still right for small tables.

The trap: what an incremental load misses

Three cases a full load handles for free and an incremental one has to plan for.

CaseWhat goes wrongThe fix
deletesA deleted source row never shows up above the watermarkSoft-delete flags, or a periodic full compare
late dataA row updated at 23:59 lands at 00:10, after the watermark movedThe overlap
no updated_atNo reliable change column, no incremental loadFix the source, or use CDC

CDC means change data capture: reading the change log of the source database instead of asking a column.

Rule of thumb: if the table fits a full load in minutes, do the full load. Incremental is for when it does not.

Frequently asked questions

What is an incremental load?
It is a pipeline that loads only the rows that changed since the last run, instead of reloading the whole table. It usually filters the source on a change column such as updated_at and writes the result with an upsert, so changed rows are updated and new rows are inserted.
What is a watermark in a data pipeline?
A watermark is the highest value of the change column that you have already loaded, for example the max updated_at in the target table. On the next run you pull only the rows above it. After the run, the watermark moves forward.
How do you handle late-arriving data in an incremental load?
Reload an overlap, for example the last two days, and dedupe on the key. A row updated at 23:59 can land at 00:10, after the watermark has moved. The overlap catches it, and the upsert makes the repeated rows harmless.
When is a full load still the right choice?
For small tables. If the table fits a full load in a few minutes, the full load is simple and correct, and it also picks up deletes. Incremental is for when a full load is too slow or too costly.

Get the free PDF

One page, print-ready, free to share. No signup needed.

Download the PDF

More cheatsheets