When “Current” Lies: How to Model Relationships That Change Over Time

A support manager opens a customer record and sees that the account belongs to the Enterprise plan.

Nothing looks wrong.

Then finance asks a different question: “Which plan was this customer on when the March invoice was issued?”

The database cannot answer.

At some point in April, someone updated customer.plan_id from 2 to 4. The current value is correct. The historical record is gone.

This is one of the more subtle problems in database design. A schema can accurately describe the system as it exists now while quietly rewriting what was true yesterday.

The problem appears whenever a relationship changes over time: employees move between departments, customers change account managers, products move between categories, patients change care teams, subscriptions switch plans, equipment changes locations, and vendors receive new contract terms.

The important modeling insight is this: sometimes a relationship is not just a foreign key. It is a fact with its own lifetime.

Start With the Question the Database Must Answer

Imagine a SaaS company with a simple customer table:

  • customer_id
  • company_name
  • plan_id
  • account_manager_id

This schema answers two questions perfectly well:

  • Which plan is the customer on now?
  • Who manages the account now?

Problems begin when someone adds two words to either question: “at the time.”

Which plan was the customer using at the time an invoice was generated?

Who owned the account at the time a renewal was negotiated?

Which department employed someone when an expense was approved?

Which warehouse held an asset when an inspection occurred?

These are not unusual reporting questions. They reveal that the database is storing a changing relationship as though it were a permanent attribute.

That is the first investigation technique worth learning: search business requirements, reports, tickets, spreadsheets, and conversations for questions containing phrases such as at that time, previously, as of, during, before, and after.

Those phrases often expose hidden temporal data requirements before anyone explicitly asks for “temporal database design.”

Investigate the Records Before Changing the Schema

Suppose the customer data currently looks like this:

  • customer_id = 418
  • company_name = Northstar Labs
  • plan_id = 4
  • account_manager_id = 73

You inspect invoices and discover:

  • January 15: invoice for $299
  • February 15: invoice for $299
  • March 15: invoice for $299
  • April 15: invoice for $899

The current plan table says plan 4 costs $899.

A developer might conclude that the January through March invoices are inconsistent. But another dataset reveals that Northstar moved from the Professional plan to the Enterprise plan on April 3.

The invoices were correct. The current customer record simply cannot explain them.

This is why database research should often precede schema changes. Looking only at table definitions would suggest that plan_id is an ordinary many-to-one relationship. Looking at historical records reveals a lifecycle.

Before redesigning the model, investigate several examples. Look for customers that upgraded, downgraded, left and returned, or switched twice in a short period. Edge cases tell you whether the relationship behaves like a simple current value or a historical fact.

The Weak Approach: Keep Overwriting the Foreign Key

The easiest implementation is also the one that destroys information:

customers.plan_id simply points to the customer’s current plan.

When the customer changes plans, the application updates that foreign key.

This approach is not automatically wrong. If the business truly cares only about the present, storing the current relationship may be sufficient.

The mistake is assuming that because an application screen shows only the current value, the business needs only the current value.

Historical questions usually arrive later through billing disputes, audits, analytics, customer support, compliance work, or product experiments.

Teams then try to reconstruct history from application logs, invoices, email notifications, backups, or timestamps in unrelated systems. The database schema has made a straightforward business question into a forensic exercise.

Turn the Relationship Into a Record

A stronger model gives the relationship its own entity.

Instead of storing only:

customers.plan_id

introduce something like customer_plan_assignments:

  • customer_plan_assignment_id
  • customer_id
  • plan_id
  • valid_from
  • valid_to
  • change_reason

Northstar’s history could now contain two records:

  • Customer 418 → Professional plan → January 8 through April 2
  • Customer 418 → Enterprise plan → April 3 onward

The relationship now has a beginning and, when applicable, an end.

This design changes what the database can know.

You can ask what is true now, but you can also ask what was true on March 15, how long the customer remained on each plan, how often customers switch, or which plan was active when another event occurred.

When visualizing this structure in an ER diagram modeling tool, the important conceptual shift is easy to see: the customer and plan are no longer connected only by a direct current-state relationship. The assignment becomes a first-class entity between them.

Challenge Another Assumption: History Is Not Always a Timestamp

Adding created_at does not automatically create historical data.

Suppose every customer record has:

  • created_at
  • updated_at

If plan_id changes on April 3, updated_at tells you that something changed. It does not tell you what changed, what the old value was, how long it was valid, or whether several fields changed together.

A timestamp on a mutable row records the age of the row’s latest version. It does not necessarily preserve the versions themselves.

This distinction matters in audit trails, temporal data modeling, and any database architecture that needs to explain past decisions.

Separate Business Time From Recording Time

Real systems introduce another complication: the moment something becomes true is not always the moment the database learns about it.

Imagine an employee transfers from Operations to Research effective June 1. HR enters the change on June 4.

There are now two relevant timelines:

  • The transfer became true in the business on June 1.
  • The database recorded the transfer on June 4.

If your model stores only created_at = June 4, a historical query for June 2 may incorrectly place the employee in Operations.

For systems where this distinction matters, temporal records may need fields representing both effective time and recording time.

For example:

  • valid_from — when the relationship became true in the real-world domain
  • valid_to — when it stopped being true
  • recorded_at — when the system learned or stored the fact

This level of modeling is unnecessary for many applications. But discovering whether it is necessary is part of good database research.

Ask whether users can enter backdated changes. Ask whether corrections are allowed. Ask whether auditors care about what the organization knew at a particular moment rather than merely what was eventually determined to be true.

Watch for Overlapping History

Once relationships have date ranges, a new class of data quality problems appears.

Suppose your assignment records say:

  • Professional plan: January 8 through April 10
  • Enterprise plan: April 3 onward

Which plan was active from April 3 through April 10?

If the business rule allows only one active subscription plan, the historical records are contradictory.

This is a useful example of how better schema design can expose rules that were previously invisible.

Investigate whether the relationship is allowed to overlap before deciding how to validate it. A patient may legitimately have multiple doctors at once. An employee may belong to several project teams simultaneously. A subscription may allow only one billing plan.

Do not apply a generic “one active row” rule simply because the tables look similar.

Sometimes You Need a Snapshot Instead of a Historical Lookup

There is another trap.

Suppose an invoice references the customer’s historical plan assignment. Years later, an administrator edits the plan’s display name from “Professional” to “Professional Legacy.”

Should an old invoice now display the new name?

Maybe not.

Certain records represent documents or facts that should remain exactly as they were when created. Invoices, signed contracts, submitted applications, tax records, and some laboratory results often belong in this category.

In those cases, a live reference to historical entities may still be insufficient. You may need to snapshot important values onto the transaction itself:

  • plan name at billing time
  • unit price
  • billing address
  • tax rate
  • contract description

This produces an important distinction in data modeling.

A historical relationship answers, “Which record was related to this entity at that time?”

A snapshot answers, “What exact values did this transaction preserve at that time?”

Those are different requirements, and many real database schemas need both.

A Practical Investigation Checklist

Before deciding that a changing relationship deserves its own historical table, trace the workflow rather than guessing from entity names.

  • Find examples where the relationship has changed in real data.
  • Ask whether anyone needs to answer “as of” questions.
  • Check whether changes can be backdated or corrected.
  • Determine whether multiple relationships may overlap.
  • Identify transactions that must preserve exact historical values.
  • Decide whether deleted or replaced relationships still matter.
  • Test the proposed model against several realistic timelines, not just one clean example.

Drawing those timelines before implementing the database schema is surprisingly effective. Put entities and relationships into an ER diagram, then walk through actual dates and events. A model that appears elegant as a static diagram may become ambiguous as soon as time is introduced.

For broader examples of relational modeling decisions, DBDesigner database design resources can provide useful context while you work through the domain-specific rules.

Model the Fact the Business Needs to Remember

Not every foreign key needs a history table. Adding temporal records everywhere creates complexity without necessarily creating value.

The better question is whether changing the relationship destroys information the organization may need later.

If nobody cares who owned a temporary draft document last Tuesday, preserving every ownership change may be pointless.

If invoices, regulatory decisions, customer entitlements, research results, or contractual obligations depend on what was true at a particular moment, overwriting the relationship is much more dangerous.

Good database engineering therefore requires more than identifying entities and drawing lines between them. You have to ask whether those lines stay true.

Some relationships describe the present.

Others describe periods of history.

Confusing the two creates databases that appear accurate until someone asks a question about yesterday.

History is not extra data. In the systems that need it, history is what prevents today’s database from rewriting the past.