Chapter 11 · Surrogate Keys, Late-Arriving Facts/Dimensions, Deletes, Restatements, and History Corrections

Late-Arriving Dimension Changes, Backdated SCD2 Splits, and Historical Restatement Policies

Apply a backdated Type 2 split, identify facts in the affected interval, restate only their dimension foreign keys through versioned revisions, and preserve metric totals.

Intermediate → Advanced125–145 minutesBackdated-history labPython stdlib sqlite3 · local/syntheticLast reviewed: September 2026

Learning outcomes

AtlasMart learns on September 22 that C001 should have been in segment Growth starting September 18 at 08:30, before the already-loaded O1001 sale. This is not a new “current” change. It is a retroactive correction to historical dimension context, so the warehouse must split the affected Type 2 interval and evaluate fact foreign keys inside that interval.

01

Apply a backdated Type 2 split without creating overlap or more than one current row.

02

Find already-loaded facts whose event timestamps fall inside the corrected interval.

03

Restate only affected dimension foreign keys through new fact revisions rather than silently rewriting old revisions.

04

Distinguish a dimensional attribution restatement from a metric-value restatement.

05

Prove totals are unchanged while segment attribution changes.

Chapter 11 continuity contract

Chapter 11 preserves all accepted AtlasMart contracts from Chapters 01–10. Before this chapter, current sales contain seven paid order-line facts, four paid orders, nine units, 625 paid GMV, 380 cost-at-sale, and 245 gross profit. Customer history uses Chapter 07's half-open business-effective intervals; Chapter 08 special dimensions, Chapter 09 bridges, and Chapter 10 fact-shape contracts remain valid. Chapter 11 adds versioned fact revisions and correction/audit surfaces without changing the business grain. A backdated C001 segment correction affects historical attribution, one late order line increases the current sales population, and one authorized O1002 amount correction changes a published metric under an explicit restatement record.

Execution, legal, and interpretation note

The mandatory lab is synthetic, local, and disposable. It uses Python's standard-library sqlite3 module and SHA-256 hashes only as reproducibility evidence. The lab proves local key-resolution, revision, temporal, and audit mechanics; it does not prove distributed exactly-once delivery, legal compliance, production deletion completeness, or cloud-warehouse behavior. Privacy/erasure requirements depend on applicable law, controller policy, purpose, retention obligations, backups/exports, and other state surfaces.

Important boundary

Backdated correction policy is a business/governance decision. The lab treats the correction as authoritative and preserves the superseded rows; another organization may require different approval, disclosure, or restatement behavior.

1. Split the interval, do not overlap it

Before correction, C001 is SMB from January 1 through September 19, then Mid-Market. The correction inserts Growth at 08:30 on September 18, producing three non-overlapping intervals: SMB before 08:30, Growth until September 19, then Mid-Market.

backdated_scd2.sql
-- conceptual result after correction-- [2026-01-01 00:00, 2026-09-18 08:30)  SMB-- [2026-09-18 08:30, 2026-09-19 00:00)  Growth-- [2026-09-19 00:00, 9999-12-31 00:00) Growth? NO -> existing Mid-Market row remains-- Integrity rules:-- every interval has effective_from < effective_to-- adjacent intervals do not overlap-- exactly one row is_current = 1 per durable customer

The correction modifies the historical interval that contained the effective timestamp; it does not replace the later Mid-Market business change.

2. Identify the facts whose keys became stale

affected_facts.sql
SELECT order_id,line_no,event_ts,customer_skFROM fact_sales_revisionWHERE is_current=1  AND durable_customer_id='D-CUST-001'  AND event_ts >= '2026-09-18T08:30:00'  AND event_ts <  '2026-09-19T00:00:00';-- O1001 line 1 and line 2 are in the corrected interval.-- Their measures do not change; only customer_sk must be re-resolved.

The executable fixture finds exactly 2 already-loaded fact rows in this interval. Each receives a new fact revision with the correct customer surrogate key; the superseded revision remains queryable for audit.

3. Restatement by new revision, not silent update

The lab marks the old fact revision non-current and inserts revision 2 with the same order-line grain, event timestamp, quantity, amount, cost, and source event ID, but the corrected customer surrogate key. A correction ledger records before/after hashes and the authorizing role.

dimension_rekey_pattern.sql
BEGIN;-- locate current fact revision-- resolve corrected customer_sk from event_tsUPDATE fact_sales_revisionSET is_current=0WHERE fact_revision_sk=:old_revision;INSERT INTO fact_sales_revision (...)SELECT ..., revision_no+1, 1, 'CORR-DIM-C001-20260918', :applied_atFROM fact_sales_revisionWHERE fact_revision_sk=:old_revision;INSERT INTO correction_ledger (...) VALUES (...before_hash..., ...after_hash...);COMMIT;

Production systems may implement this with different SQL/transaction primitives. The invariant is versioned, attributable change—not a specific vendor MERGE syntax.

4. Totals must remain invariant

Stage Current lines Orders Units GMV Cost Gross profit
Accepted Chapter 10 baseline 7 4 9 625 380 245
After backdated dimension split + key restatement 7 4 9 625 380 245
After late O1000 line arrives 8 5 10 700 425 275
After authorized O1002 amount restatement 8 5 10 690 425 265
After source-delete + privacy workflow 8 5 10 690 425 265

After the dimension split and two key restatements, sales controls are still 7 lines, 4 orders, 9 units, 625 GMV, 380 cost, and 245 gross profit. The correction changes historical slicing, not measurement values.

5. Deliberately wrong approaches

Wrong 1: append a backdated Type 2 row without closing the covering interval; as-of joins can then return multiple customer rows. Wrong 2: change only the dimension and leave affected facts pointing at the obsolete surrogate key; totals reconcile but historical attribution remains wrong. Wrong 3: rewrite old fact foreign keys in place and destroy evidence of what was previously published.

6. Restatement scope and downstream impact

Before applying a retroactive dimension correction, identify affected facts, aggregates, semantic metrics, extracts, caches, and reports. Some systems materialize segment-level aggregates; they may need rebuild even when atomic amounts do not change. Publish a correction batch ID so consumers can trace why historical slices moved.

Knowledge check

Check your understanding

  1. Why does a backdated Type 2 correction require interval splitting?
  2. Which O1001 fields change during dimension-key restatement?
  3. Why are unchanged grand totals insufficient evidence?
  4. What is preserved by inserting a new fact revision?
  5. What downstream objects may need refresh after a dimension restatement?
Review the answers

1. Because the corrected attribute becomes effective inside an existing interval; the warehouse must preserve non-overlapping historical coverage.

2. Only the dimension foreign key/revision metadata; quantity, revenue, cost, event time, and business grain remain unchanged.

3. Historical slices can still be attributed to the wrong version even when aggregate measures reconcile.

4. The previously published foreign-key state remains auditable instead of being silently overwritten.

5. Aggregates, semantic caches, extracts, reports, or any derived surface whose grouping depends on the corrected attribute.

Summary and next step

Retroactive dimension history is a governed restatement, not a routine append. Lesson 4 turns to source deletion and privacy erasure, where “delete” can mean very different things operationally and legally.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.