lesson 85 Warehouse & reporting projects · advanced
Idempotent incremental UPSERT & stale updates
Repeat incremental updates safely and reject stale changes.
the idea
An incremental load inserts new keys and updates only newer source versions. Replay the same batch and the final rows must stay the same. A timestamp watermark alone can miss late records or equal timestamps; real pipelines need a defined tie-breaker/replay window, delete handling, and transactional checkpointing.
tables in this lesson
The challenges use this data. Each column shows its type underneath; → marks a link to another table.
incoming_people3 columns · 3 rows
| customer_idtext · key | nametext | updated_attext |
|---|---|---|
| C001 | Alice New | 2025-02-01 |
| C002 | Ben stale | 2024-01-01 |
| C006 | Finn | 2025-02-02 |
all 3 rows
current_people4 columns · 2 rows
| customer_keyinteger · key | customer_idtext | nametext | updated_attext |
|---|---|---|---|
| 1 | C001 | Alice Nguyen | 2025-01-05 |
| 2 | C002 | Ben | 2025-01-02 |
all 2 rows
example
The idea in one query. Press Run to see what it returns; running examples never affects your progress.
INSERT INTO current_people(customer_id,name,updated_at) SELECT customer_id,name,updated_at FROM incoming_people WHERE 1 ON CONFLICT(customer_id) DO UPDATE SET name=excluded.name,updated_at=excluded.updated_at WHERE excluded.updated_at>current_people.updated_at; INSERT INTO current_people(customer_id,name,updated_at) SELECT customer_id,name,updated_at FROM incoming_people WHERE 1 ON CONFLICT(customer_id) DO UPDATE SET name=excluded.name,updated_at=excluded.updated_at WHERE excluded.updated_at>current_people.updated_at; SELECT customer_id,name,updated_at FROM current_people;
Watch for: Do not use INSERT OR REPLACE as a synonym: replacement can delete/reinsert and break keys. This demonstration is not CDC or orchestration.
build it step by step
Each step changes one thing. Run them in order and watch the result change.
Understand the source contract. Fictional extracts, load counts, invalid dates, versioned dimensions, refresh staging and a data catalog. The foundation project is latest-state; separate history and incremental labs demonstrate extensions.
step-1.sqlSELECT * FROM incoming_people;
Execute the transformation. An incremental load inserts new keys and updates only newer source versions. Replay the same batch and the final rows must stay the same. A timestamp watermark alone can miss late records or equal timestamps; real pipelines need a defined tie-breaker/replay window, delete handling, and transactional checkpointing.
step-2.sqlINSERT INTO current_people(customer_id,name,updated_at) SELECT customer_id,name,updated_at FROM incoming_people WHERE 1 ON CONFLICT(customer_id) DO UPDATE SET name=excluded.name,updated_at=excluded.updated_at WHERE excluded.updated_at>current_people.updated_at; INSERT INTO current_people(customer_id,name,updated_at) SELECT customer_id,name,updated_at FROM incoming_people WHERE 1 ON CONFLICT(customer_id) DO UPDATE SET name=excluded.name,updated_at=excluded.updated_at WHERE excluded.updated_at>current_people.updated_at; SELECT customer_id,name,updated_at FROM current_people;
Test another question. Do not use INSERT OR REPLACE as a synonym: replacement can delete/reinsert and break keys. This demonstration is not CDC or orchestration.
step-3.sqlINSERT INTO current_people(customer_id,name,updated_at) SELECT customer_id,name,updated_at FROM incoming_people WHERE 1 ON CONFLICT(customer_id) DO UPDATE SET name=excluded.name,updated_at=excluded.updated_at WHERE excluded.updated_at>current_people.updated_at; SELECT customer_id,customer_key FROM current_people