+91 98726 60544 hello@mitstech.co Mon–Sat · 09:00–18:30 IST

Data modelling: normalise first, denormalise deliberately

Data & AI By Mits Engineering Team 2 min read
Data modelling: normalise first, denormalise deliberately

Normalisation has an unfashionable reputation, usually justified by performance. The argument is that joins are expensive and duplicating data avoids them. It is true in specific circumstances and false as a general rule, and adopting it early costs far more than the joins ever would - because every duplicate is a place where the truth can diverge.

The reason to normalise is not elegance, it is that each fact lives in exactly one place. When a customer changes their address, one row changes and every query sees it. When the address is copied into orders, invoices and shipping records, some of those are now wrong, and no constraint in the database will tell you which. That is not a performance trade-off; it is trading correctness for speed, and it should be made consciously and rarely.

There are legitimate reasons to duplicate, and it is worth naming them so the decision is deliberate. Historical accuracy: an invoice should record the address it was actually sent to and the price actually charged, because those are facts about the transaction rather than references to a customer record that will change. That is not denormalisation - it is capturing a different fact, and it is correct.

Genuine read performance is the other, and it should be evidence-driven. If a query joining six tables is measurably too slow on real data volumes after indexing has been done properly, then a materialised view or a summary table is a reasonable answer. The order matters: measure, index, examine the plan, and only then duplicate. Denormalising before any of that is optimising a problem you have not confirmed exists.

When you do duplicate, make the update path a single one. A denormalised value updated from three different code paths will eventually be updated by two of them and missed by the third. Triggers, materialised views or a single service that owns the write are all acceptable; scattering the responsibility across the application is what turns a performance optimisation into a data-integrity incident eighteen months later.

Two things worth deciding early because they are expensive to change. Whether you need history: if anyone will ever ask what this record looked like in March, that requirement must be in the model from the start, because you cannot reconstruct history from a table that only stores the present. And use constraints - foreign keys, unique indexes, check constraints, not null - rather than enforcing rules only in application code. Applications get bypassed by scripts, migrations and admin tools; the database does not.

Need help with this? Explore our Data Analytics & BI services. Learn more Back to all news

Keep reading

More on Data & AI