runtime
85 / 88

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 existing ones in one statement: INSERT … ON CONFLICT(customer_id) DO UPDATE SET … . Guard the update with WHERE excluded.updated_at > current_people.updated_at so stale records cannot overwrite newer data, and the same batch can be applied twice with the same result.

Dataset: Retail

The tables the challenges use, with their first 6 rows. Under each column name: the kind of value it holds, and which table it links to.

current_people4 columns · 40 rows
customer_keynumber · unique idcustomer_idtextcontact_nametextupdated_atdate & time
112347Arthur Turner2011-11-30 18:00:00
212353Harry Turner2011-11-30 18:00:00
312359Theo Turner2011-11-30 18:00:00
412373Harry Hill2011-11-30 18:00:00
512374Emily Hill2011-11-30 18:00:00
612384Ava Moore2011-11-30 18:00:00
customer_keynumber · unique id
1
customer_idtext
12347
contact_nametext
Arthur Turner
updated_atdate & time
2011-11-30 18:00:00
customer_keynumber · unique id
2
customer_idtext
12353
contact_nametext
Harry Turner
updated_atdate & time
2011-11-30 18:00:00
customer_keynumber · unique id
3
customer_idtext
12359
contact_nametext
Theo Turner
updated_atdate & time
2011-11-30 18:00:00
customer_keynumber · unique id
4
customer_idtext
12373
contact_nametext
Harry Hill
updated_atdate & time
2011-11-30 18:00:00
customer_keynumber · unique id
5
customer_idtext
12374
contact_nametext
Emily Hill
updated_atdate & time
2011-11-30 18:00:00
customer_keynumber · unique id
6
customer_idtext
12384
contact_nametext
Ava Moore
updated_atdate & time
2011-11-30 18:00:00

first 6 of 40 rows · your queries use all of them

incoming_people3 columns · 8 rows
customer_idtextcontact_nametextupdated_atdate & time
12359Theo Turner (updated)2011-12-05 08:00:00
12392Lily Moore (updated)2011-12-05 08:00:00
12447Arthur Taylor (updated)2011-12-05 08:00:00
12507Arthur Wilson (updated)2011-12-05 08:00:00
12374Emily Hill (stale)2011-11-15 08:00:00
12566Mia Patel (stale)2011-11-15 08:00:00
customer_idtext
12359
contact_nametext
Theo Turner (updated)
updated_atdate & time
2011-12-05 08:00:00
customer_idtext
12392
contact_nametext
Lily Moore (updated)
updated_atdate & time
2011-12-05 08:00:00
customer_idtext
12447
contact_nametext
Arthur Taylor (updated)
updated_atdate & time
2011-12-05 08:00:00
customer_idtext
12507
contact_nametext
Arthur Wilson (updated)
updated_atdate & time
2011-12-05 08:00:00
customer_idtext
12374
contact_nametext
Emily Hill (stale)
updated_atdate & time
2011-11-15 08:00:00
customer_idtext
12566
contact_nametext
Mia Patel (stale)
updated_atdate & time
2011-11-15 08:00:00

first 6 of 8 rows · your queries use all of them

worked example

Run these steps in order to see the idea before you write your own query. The step marked “the pattern” is what the challenges ask for. Running the example does not count toward progress.

  1. Compare incoming with current timestamps. Side-by-side updated_at values show new keys, stale replays, and rows that should win on conflict.the pattern

    example.sql
    SELECT i.customer_id, i.updated_at AS incoming, c.updated_at AS current
    FROM incoming_people i
    LEFT JOIN current_people c ON c.customer_id = i.customer_id;
  2. Count keys that would conflict. These incoming customer_ids already exist—rerunning a plain INSERT would fail without ON CONFLICT.

    step-2.sql
    SELECT COUNT(*) AS would_conflict
    FROM incoming_people i
    JOIN current_people c ON c.customer_id = i.customer_id;
  3. See which incoming rows are newer. The newer flag is the UPSERT guard: only when incoming updated_at beats current should contact_name and updated_at change.

    step-3.sql
    SELECT
      i.customer_id,
      i.updated_at AS incoming,
      c.updated_at AS current,
      i.updated_at > c.updated_at AS newer
    FROM incoming_people i
    JOIN current_people c ON c.customer_id = i.customer_id;

common mistake

Without the timestamp guard, replaying an old record overwrites a newer value; without ON CONFLICT, rerunning the batch fails on duplicate keys.

challenge 1

Apply incoming_people to current_people with INSERT … ON CONFLICT(customer_id) DO UPDATE, updating contact_name and updated_at only when the incoming updated_at is newer.

Apply the same statement twice.

Return customer_id, contact_name and updated_at for all rows.

what to return

Columns
customer_idcontact_nameupdated_at in this order
Rows
any order
Also checked
TEMP / table changes from your script
challenge-1.sql
 
To master this lesson, solve both challenges without hints and pass the concept check. 3 of 3 to go.
Idempotent incremental UPSERT & stale updates — Warehouse & reporting projects · Runtime