THIS EXPLANATION
THE ROOM
CDA·178 Computing, Data & AI 7 MIN · 8 STATIONS

Recording a change without losing the present

A Socratic walk-through of recording a change without losing the present — reasoned out one step at a time, not lectured.

abcdefgh
a

The question we started with

THE QUESTION #

How can a table show both what a customer's details are now and what they were when they ordered?

A customer moves from the Northern region to the Southern one. The obvious thing to do is update the row: set region = 'South', done. The table now tells the truth.

But last year's report, which said that the North sold four million, will now say something different when you re-run it — because every order that customer ever placed has quietly relocated. Nothing was deleted. No fact about any order changed. Yet the past moved. How can a table be truthful about the present without rewriting the past, when it has only one row per customer?

b

Reasoning it through

REASONING #

Look closely at what the update destroyed. It did not destroy the region — the region is still there. It destroyed the pairing of a region with a stretch of time. region = 'North' was never simply true; it was true from 2019 until March. Storing it as a bare attribute throws away the interval, and once the interval is gone, there is no way to ask what was true in June.

So if the interval is the missing information, the fix must be to store it. And notice what that immediately implies: a single row cannot hold two intervals. If the customer was North and then South, that is two facts, and two facts need two rows.

Follow that through. Instead of updating, we close the existing row — stamping it with the date its truth ended — and insert a new one that begins where the old one stopped. The customer now occupies two rows: one describing them as they were, one as they are, each carrying the window in which it applied.

Two things follow that are worth pausing on. First, the customer's business identifier is now duplicated across rows, so it can no longer identify a row. Something else must — which is exactly the job a meaningless minted key does, one per version. Second, "which row is the current one?" has become a real question. It is answerable from the intervals alone — the row whose end date is still open — but because that question is asked constantly, tables usually carry an explicit flag as well, accepting the redundancy for the sake of a simple filter.

Now the payoff. An order recorded in February does not merely reference the customer; it references the version of the customer that was current in February. The order carries that version's key. Re-run last year's report in any future year and the North still sold four million, because the order is bolted to the row that described the customer at the time. Meanwhile a report on customers as they are today joins on the current row and sees the South. Both questions are answerable, from the same table, without either answer disturbing the other.

Is that always what you want, though? Consider a different change: someone had misspelled the customer's name, and it has been corrected. Should there be a version of the customer where the name was wrong? Almost certainly not — that is not a change in the world, it is a repair of the record, and preserving it would only invite the question of which spelling last year's report should use. So a correction is overwritten in place, everywhere, while a genuine change of state is versioned. The difficult part in practice is that the table cannot tell the two apart. Deciding which columns represent facts that changed and which represent facts that were wrong is a modelling judgement, made per column, in advance — and getting it wrong is discovered years later when a report cannot be reproduced.

There is a third arrangement, less used and worth knowing: keeping a previous value column beside the current one. It answers "what was it before?" without creating rows, which keeps the table small and the joins simple, but it remembers exactly one step back and knows nothing of when the change happened. It suits a case where one recent prior value is genuinely all anyone asks for.

A caution on cost. Versioning multiplies rows, and a column that changes often can produce so many versions that the description outgrows the facts it describes. The usual remedy is to move such columns somewhere designed for high-frequency change, not to give up versioning the rest.

c

The analogy

THE ANALOGY #
THE FIGURE

Think of a passport rather than a driving licence photograph. When your address changes, a driving licence is reissued with the new one and the old card is destroyed — the document always describes you now, and cannot help with where you lived in 2019. A passport instead accumulates: each stamp records not just a country but the day, so the book as a whole is a record of where you were at any given time, while the biographical page still says who you are today.

WHERE IT BREAKS DOWN

passport stamps are added by strangers and can never be revised, whereas a versioned table must still allow genuine corrections to be overwritten — so the discipline it needs is not "never change anything" but "know which columns are corrections and which are history".

d

Clarifying the model

THE MODEL #

Three refinements.

First, the misconception that this is an audit log. An audit log records that a change was made, by whom and when, and is read by humans investigating. Versioned rows exist so that ordinary queries land on the right version automatically. They serve analysis, not accountability, and a system may well want both.

Second, this technique does not, by itself, get facts pointed at the right version. It only makes the right version exist. Something must decide, at the moment an order is recorded, which version key to attach — and for data arriving late, that decision is not the current row but the row that was current when the event occurred.

Third, the interval and the flag must agree. Two independent representations of the same truth is a duplication that drifts: a load that inserts a new version but forgets to close the old one produces two overlapping current rows, and every downstream join silently doubles. It is worth constraining or checking rather than trusting.

e

A picture of it

THE PICTURE #
Recording a change without losing the present
Recording a change without losing the present read left to right as real elapsed time, and treat each section as one era of the same customer. Notice that the middle section contains two entries on the same date -- closing the old row and opening the new one are a single indivisible act, and doing one without the other is the failure mode described above. The final entry is the point of the whole arrangement: orders never move between eras, because each was attached to the version that was current when it happened. {"generator":"mermaid-svg-renderer@3.2.1","source":"../Socrates/.diagram-cache/_src/recording-a-change-without-losing-the-present.md","sourceIndex":1,"sourceLine":4,"sourceHash":"a76202a2a7bb88684bdf8f35ff465c2a86a98aaa06a5de3e3ff0f1e46f4d7913","diagramType":"timeline","layoutVariant":"source","repairedDuplicateIds":[],"motion":"entrance-with-reduced-motion-fallback","presentation":"editorial","attempt":1,"viewBox":{"x":0,"y":0,"width":1555,"height":726},"qa":{"passed":true,"findings":[]}} Version A open 2019 Row A insertedwith region North end date left open flagged current 2020 Orders placed nowcarry the key ofrow A Version A closed 2021 March Customer movesSouth row A stampedwith an end date flag cleared 2021 March Row B inserted withregion South open end date flagged current Version B current 2021 April New orders carrythe key of row B 2022 Old orders stillcarry row A so theNorth total isunchanged

How to readread left to right as real elapsed time, and treat each section as one era of the same customer. Notice that the middle section contains two entries on the same date — closing the old row and opening the new one are a single indivisible act, and doing one without the other is the failure mode described above. The final entry is the point of the whole arrangement: orders never move between eras, because each was attached to the version that was current when it happened.

f

What became clearer

WHAT CLEARED #
WHAT CLEARED

Updating a row does not lose data so much as lose the time attached to it — and once the interval is gone, the past cannot be reconstructed. Keeping a row per era, each stamped with the window in which it applied, lets the same table answer both "how are things now" and "how were they then", because facts are attached to versions rather than to entities. The price is a table that grows with change, a per-column judgement about what counts as history versus a correction, and the discipline of never opening a new version without closing the old one.

g

Where to go next

ONWARD #
  • How a fact arriving late is matched to the version that was current when it happened.
  • Which columns deserve versioning, and where fast-changing attributes should live instead.
  • What breaks when two versions of one entity are both flagged current.
h

Key terms

TERMS #
TermWhat it means
Slowly changing dimensiona descriptive table whose attributes change occasionally, requiring a policy for what happens to history.
Row versioningstoring one row per era of an entity, each with the interval in which it applied.
Valid-from and valid-tothe stamps marking the window during which a version described the entity.
Current flaga redundant marker identifying the version with an open end date, kept for query simplicity.

Every term the collection defines is gathered in the glossary.

Nearby on the shelf

4