How can we turn messy spreadsheet data into a relational model? (pt. 2)

Relational databases are the backbone of most systems that need to track structured, interconnected information reliably. E-commerce platforms use them to link customers, orders, and inventory. Banks use them to tie accounts, transactions, and balances together in a way that can't drift out of sync. All this data is being relied on to make significant decisions, which is why normalization is used.

In order for RDBMS (relational database management systems) to maintain their integrity these models adopt a practice called normalization which is the process of organizing tables so that each piece of data is stored in exactly one place, based on a set of rules called normal forms. Each normal form fixes a specific kind of redundancy or inconsistency the previous form still allows.

Let's look at the sheet below:

fig.1

In the flat spreadsheet, finding every bid placed by user "A. Kim" means scanning every row, checking the bidder column on each one, and the seller/item info riding along for the ride even though it's irrelevant to that question. As the sheet grows to thousands of rows, that scan gets linearly slower.

Furthermore every bid repeats the seller's name and the bidder's email, which means if a bidder ever needs to update their email, we'd have to find and update it in every single row they've ever appeared in otherwise we risk inconsistency.

This is what the normalized tables for fig. 1 would look like:

Items table ( dimension table )
Bids table ( main table, the fact table) item_id is a foreign key referencing to the item_id(PK) on the items table. bidder_id is also a foreign key referring to user_id (PK) in the Users table).
Users table (dimension table)

Side note: bidder_id and seller_id are both referring to the users table, they're just named differently to make it easier for the user to understand.

What happened here is that the single spreadsheet got split into three tables: users, items, and bids, each responsible for exactly one kind of fact. "Users" holds each person's name and email exactly once, whether they're acting as a seller or a bidder. "Items" holds each item's description once, along with a "seller_id" pointing back to whoever's selling it. Bids holds only what a bid actually is: which item it's for, who placed it, and the amount, referring to the other two tables through "item_id" and "bidder_id" instead of repeating their details.

After normalization, the bids table only contains what a bid actually is, a user_id, an item_id, a bid amount. Because each table is now smaller and each column serves one clear purpose, the database can use indexes on keys to jump directly to matching rows instead of scanning every field of every row for a match. Querying "all bids by this user" becomes a fast lookup against a small bids table instead of a slow read through a bloated sheet where that user's info is duplicated a hundred times over.

The payoff shows up the moment something needs to change or be looked up. Update J. Reyes's email once, in one row of users, and every item they've listed and every bid tied to them reflects it automatically, there's no longer a hundred copies to hunt down, and no risk of the same person ending up with two different emails in the system. That's data integrity in practice: the data can only be stored one true way, so there's nothing left to fall out of sync.

In the next blog post we’ll dive further into how relational databases are used today.

Author:
Dev Bhatnagar
Powered by The Information Lab
1st Floor, 25 Watling Street, London, EC4M 9BR
Subscribe
to our Newsletter
Get the lastest news about The Data School and application tips
Subscribe now
© 2026 The Information Lab