In an SCD Type 2 load, what does a source updated_at strategy miss that comparing tracked columns catches?
answer
- who maintains the column you are trusting?
- a bulk UPDATE that skipped the trigger
- the stamp moved but nothing tracked changed
- cheap filter versus full scan
- two changes between runs, one version
basics
~20 sAn updated_at strategy trusts the source to stamp every write, so it misses bulk backfills, trigger-bypassing writes and rows with a NULL timestamp, and it creates spurious versions when the stamp moves without a tracked attribute changing. Comparing tracked column values depends on nothing the source maintains.
solid answer
~50 sTimestamp-based detection asks the source "did this row change?" and believes the answer. It is cheap — you can filter the extract on `updated_at > last_run` and never read unchanged rows — but it inherits every gap in the source's discipline: migrations and bulk `UPDATE`s that skip the trigger, rows loaded before the column existed, NULL stamps, and clock skew across replicas all produce changes the load never sees. It also over-fires: a write that only touched an untracked column bumps the stamp, and the load versions a member for nothing. Comparing the tracked attributes — usually via a hashdiff — asks the data itself and needs no source cooperation, at the cost of reading the whole current snapshot every run. What neither gives you is intermediate history: both are snapshot comparisons, so two changes between runs collapse into one version. Only a change-data-capture feed records every state.
code
text · 5 linessource row, run N : id=42 tier=Gold sync_status=OK updated_at=2026-03-01 02:00
source row, run N+1 : id=42 tier=Gold sync_status=RETRY updated_at=2026-03-02 02:00
timestamp strategy -> stamp advanced -> closes v1, inserts v2 (tier still Gold)
check strategy -> tier unchanged -> writes nothinggo deeper
Know the two ways a load decides a dimension row changed: trusting a source-maintained modified timestamp, or comparing the attribute values themselves against what is already stored.
Be able to state the tradeoff concretely — a timestamp filter reads only candidate rows but inherits the source's gaps, while value comparison needs no source cooperation but scans everything each run.
Expect the scenario where a dimension quietly stopped versioning after a source migration. Trace it to a bypassed timestamp, and describe the reconciliation you would add so the next gap is detected rather than discovered by a business user.
Own the reliability position: decide platform-wide whether source-maintained change metadata is trusted, what evidence justifies trusting it, and when the answer is to require a change-data-capture feed from the source team instead.
## Two ways to answer "what changed" Every Type 2 load needs a changed set, and there are two families of answer. **Timestamp strategy**: trust a source-maintained column — `updated_at`, `modified_ts`, a row version — and treat any row whose stamp advanced since the last run as changed. **Check strategy**: compare the current values of a declared list of tracked columns against what the dimension already stores, usually through a hashdiff, and treat a difference as a change. Snapshot-based transformation tools expose exactly this choice, and interviewers ask about it because the failure modes are asymmetric and both are real. ## Where the timestamp strategy fails **Missed changes — the dangerous direction.** The stamp is maintained by the source, so it is only as reliable as the source's discipline: - A data migration or a one-off `UPDATE ... WHERE` issued by an operator often bypasses the application code that sets the column, and some sources deliberately suppress triggers during bulk loads. - Rows written before the column was introduced carry NULL, and `NULL > last_run` is unknown, so those rows are invisible forever. - Deletes never bump a timestamp on a row that is gone. - Clock skew between writers, or a stamp taken at transaction start rather than commit, can place a change just before your watermark; the next run's `>` filter then steps over it. Overlapping the watermark by a safety margin and relying on the load's idempotence to absorb the re-read is the usual mitigation. A missed change is silent. The dimension simply keeps reporting an old attribute value, and nothing in the pipeline errors. **Spurious versions — the noisy direction.** The stamp records that *some* column changed, not that a *tracked* one did. A source that rewrites a `last_seen` or `sync_status` column on every sync bumps `updated_at` for every row, and a timestamp-driven load then versions the entire dimension nightly with identical attributes. You end up paying for history that records nothing. ## Where the check strategy fails It is not free. Comparing values means reading the full current state of the source table on every run — you cannot push a cheap watermark filter down to the extract — so the cost scales with dimension size rather than with change volume. On a large dimension that is the whole argument for the timestamp approach. It is also only as good as its tracked list: a change outside that list is invisible by design. That is a feature (audit columns are excluded on purpose) but it means "the check strategy catches everything" is wrong as stated — it catches everything *you declared*. And because the stored hash encodes the tracked list implicitly, editing the list re-versions the whole dimension on the next run. Finally, it depends on being able to see the row at all. If the source snapshot is filtered, a member's absence is indistinguishable from a member that never changed. ## What both strategies miss Both are **snapshot comparisons at run boundaries**. If a customer's tier goes Silver → Gold → Silver between two nightly runs, the timestamp strategy sees a bumped stamp and writes a version whose attributes equal the previous one; the check strategy sees no difference and writes nothing. Neither records the Gold interval, because neither ever observed it. Increasing run frequency narrows the window but never closes it. Recording every intermediate state requires a source that emits changes rather than states — a change-data-capture stream or an application-maintained change log — replayed in order. That is a source-side decision, and it is worth naming in an interview: if the business genuinely needs every state, no snapshot cadence will deliver it. ## Choosing A workable default: use the check strategy unless the dimension is large enough that a full scan hurts, because it depends on nothing outside your control. Use the timestamp strategy when the source's stamp is genuinely trustworthy and the volume argument is real — and then verify it periodically with a reconciliation run that does a full value comparison and reports any row the timestamp path would have missed. Hybrid forms exist: filter the extract by timestamp for volume, then still compare hashes before writing, so a spurious stamp bump costs a read but not a version.
- How would you validate that a timestamp-driven load is not missing changes?Run a periodic reconciliation: take a full snapshot of the source, compute the tracked-column hash for every row, and compare against the dimension's current versions. Any mismatch is a change the timestamp path missed. Schedule it weekly or monthly, alert on the count, and treat a persistent non-zero result as a source-side defect to fix, not a load to patch.
- Can you combine the two strategies?Yes, and it is often the best answer. Use the timestamp to filter the extract so you read only candidate rows, then still compare the tracked-column hash before writing. You keep the volume saving while spurious stamp bumps cost a read instead of a bogus version — but you still inherit the timestamp's missed-change risk, so pair it with the reconciliation run.
- Why does raising the load frequency not fix missed intermediate states?Both strategies compare states at run boundaries, so any change that occurs and reverses within one interval is invisible however short the interval is. Running hourly instead of nightly narrows the window, it does not close it. Capturing every state requires a source that emits changes — a CDC stream or a change log — replayed in order.
saying these in an interview costs you the question
- Assuming the source always maintains updated_at correctly
- Claiming column comparison catches changes outside the tracked list
- Believing more frequent runs capture intermediate states
- Filtering the extract on updated_at > last_run with no overlap
- Ignoring that a bumped stamp may touch no tracked attribute