Question presented to candidate: "Your price feed sends events out of order, and an older price sometimes overwrites a newer one. Separately, product wants a 'welcome' email only when a customer row is created, not when it is updated, and customers are soft-deleted with a deleted_at column, so a person who deleted their account must be able to sign up again with the same email. How do you handle all three with upserts?"
What a strong answer should cover:
- Conditional upsert:
ON CONFLICT ... DO UPDATE SET ... WHERE excluded.source_ts > prices.source_ts(newest wins) orWHERE t.col IS DISTINCT FROM excluded.col(skip no-op writes); rows whoseWHEREis false are not updated and not returned byRETURNING, but they are still locked - Insert-or-update detection in PostgreSQL:
RETURNING (xmax = 0) AS inserted; whatxmaxis, why it is 0 for a new row and non-zero after theDO UPDATEpath, and that this is an implementation detail rather than a documented API - Portable alternatives: a
version(orcreated_at = updated_at) column, orMERGE ... RETURNING merge_action()on PostgreSQL 17+ - Soft delete: a partial unique index
UNIQUE (email) WHERE deleted_at IS NULL, and an upsert whose conflict target repeats the predicate:ON CONFLICT (email) WHERE deleted_at IS NULL - What breaks: a conflict target without the predicate does not match the partial index and is rejected; a plain
UNIQUE (email)constraint would block re-signup
Clarifying questions expected:
- Does every event carry a reliable timestamp or version to compare against?
- Does "created" need to be known only in the same statement, or later too?
- Should a re-signup revive the old soft-deleted row or create a new one?
Code / implementation expected: Yes — a conditional DO UPDATE ... WHERE, a RETURNING that tells inserts from updates, and a partial unique index used as a conflict target.
Schema used by the examples: