Foreign Keys & Referential Integrity

5.Tabs for Customers Who Don't Exist

M

In this chapter

We'll see what happens when a customer gets deleted but their unpaid tab doesn't — and meet the foreign key link that stops that from happening silently.

10–12 min

The Problem in Real Life

Mike spends a slow afternoon tidying up the Customers table. Maria hasn't shopped at GreenMart in over a year — so Mike deletes her row. Feels like cleanup, not damage.

A week later, Sarah pulls up the sales report and finds a purchase — a loaf of bread, still unpaid — attached to Customer ID 103. There's no Customer ID 103 anymore. There's no customer to even ask for the money.

Table — Customers — after the delete (before/broken)
Customer IDCustomerPhone
101Alex555-0198

Maria — Customer ID 103 — used to be here. Mike deleted her row last week, without checking anything else first.

Table — Sales — still pointing at 103 (before/broken)
Customer IDProductPriceDate
101Apples$4Aug 2
103Bread$3Aug 23
"Customer ID" is a column — every row has this fact

This row still says Customer ID 103 — but 103 doesn't exist in Customers anymore. It's pointing at nothing.

S

Who even is customer 103 anymore?

Sarah

Orphaned Row vs. Foreign Key

A link points at nothing

The Sales row still says Customer ID 103 — but 103 doesn't exist anymore.

Reports stop making sense

"Who bought this?" no longer has a real answer.

Nothing warns you it happened

The delete succeeds instantly and silently — nobody notices until someone hits the orphaned row.

One orphan invites more

Once one dangling reference exists, nothing stops a second, or a whole pattern of them.

What Are Foreign Keys and Referential Integrity, Really?

To the database, Sales.Customer ID was never really "a link to a customer" — it was just a number, the same as Price or Quantity. Nothing about deleting a row in Customers automatically checks whether some other table still mentions it. Mike's delete succeeded instantly, with no warning, because nothing was watching for this at all. A row like Maria's old sale — pointing at an ID that no longer exists — is called an orphaned row.

The fix has a name: a foreign key. A foreign key is a column in one table that's meant to match a primary key in another table — here, Sales.Customer ID pointing at Customers.Customer ID. On its own, that's just a description. What makes it useful is the promise attached to it: referential integrity — the guarantee that a foreign key's value always matches a real, existing row in the other table, enforced by the database itself, every single time, not by whoever happens to remember to check.

Once Sales.Customer ID is declared as a real foreign key, the database starts paying attention to exactly the situation Mike just walked into — someone deleting a row that something else still depends on. And it gives GreenMart an actual choice about what should happen next:

  • RESTRICT — block the delete outright while anything still references that row. This is what GreenMart should choose here: losing track of who owes money is worse than a delete failing.
  • CASCADE — delete the dependent rows too. Convenient, but dangerous for something like sales history — it would have erased Maria's entire purchase record along with her.
  • SET NULL — keep the dependent row, but blank out the link. The sale would survive, just with no customer attached — better than an orphan, but still a fact quietly lost.
Table — What Happens Now — after (RESTRICT enforced)
Attempted ActionResult
DELETE Customer 103 (Maria)Blocked — still referenced by 1 sale
DELETE Customer 101 (Alex)Allowed — no sales reference this customer
This whole line is a row — one record

The exact mistake that opened this chapter can't happen silently anymore. Deleting a customer with no open sales still works fine — it's only blocked when something would be left pointing at nothing.

For GreenMart, RESTRICT is the obvious choice: a delete that fails with a clear reason is a minor annoyance. A sale silently pointing at nobody is a real accounting problem, discovered weeks later by accident. With referential integrity turned on, trying to delete Maria while Bread's sale still references her wouldn't quietly succeed anymore — the database would refuse, and say exactly why.

Notice what actually changed here: nothing about the Customers or Sales tables themselves. The columns are the same. What's different is a rule the database now enforces on GreenMart's behalf, every time, without anyone having to remember to check by hand.

Key Takeaway

A foreign key isn't just a link between two tables — it's a promise the database enforces: nothing is ever allowed to point at a row that doesn't exist.

Why This Matters

This is the exact mechanism every join in Act 3 depends on — a join only works because a foreign key reliably points at a real row somewhere else. It's also the reason a schema evolves carefully instead of freely: once other tables reference a table's primary key, deleting or restructuring that table has consequences the database will now actively enforce, not just quietly allow.

GreenMart's tables can no longer silently disagree about whether a customer exists. But nothing yet stops someone from leaving a phone number blank, typing a negative price by mistake, or adding a sale with no date at all — that's exactly where the next chapter starts.

Next