skip to content

Why is an analytics model deliberately denormalized when its OLTP source schema is normalized?

level: juniorimportance: must knowfreq 82%

answer

  1. two workloads, two contracts
  2. who writes it, and how often?
  3. constraints protect writers, not readers
  4. count the joins per business question

basics

~20 s

The two schemas are optimized for different jobs. A transactional schema normalizes so concurrent single-row writes stay consistent with one place per fact. An analytics model flattens attributes onto few wide tables so large read-only scans need fewer joins.

solid answer

~50 s

They are optimized for opposite workloads. An OLTP schema normalizes so that every fact lives in exactly one place: many concurrent transactions write single rows, and a single point of maintenance plus database constraints is what keeps them consistent. An analytics model is **loaded, not transacted** — one controlled ELT job writes it, and hundreds of read-only queries scan it. There, storage is cheap and joins are expensive, so we collapse a source's lookup chain into one wide dimension row and store measures at a declared grain in a fact table. What you buy is fewer joins per question, stable column names analysts can learn, and query text that no longer re-implements source-system logic. What you pay is extra storage, a load process that must reproduce the flattened values correctly, and the loss of database-enforced integrity — which is why analytics teams replace constraints with tests over the loaded model.

code

text · 9 lines
text
-- Source (normalized): four tables stand between an order and a region
customer(customer_id, name, city_id)
city(city_id, city_name, region_id)
region(region_id, region_name, country_id)
country(country_id, country_name)

-- Warehouse dimension: the chain is resolved once, at load time
dim_customer(customer_key, customer_id, name,
             city_name, region_name, country_name)

go deeper

for a junior

Be ready to state the contrast crisply: transactional schemas store each fact once so concurrent writes stay consistent; analytics models flatten attributes so read-only queries need fewer joins. Name one benefit and one cost of the flattening.

for a middle

Explain the mechanism, not just the slogan: one controlled batch writer is what makes redundancy safe, and the copied attributes are derived data that a re-run of the load can recompute.

for a senior

Show that you know what you gave up. Constraint enforcement disappears, so describe what you put in its place and how you keep the flattened model in agreement with a source you do not control.

for a principal

Own the framing that the two shapes serve two contracts, and be able to argue where the boundary belongs in your platform: which layers mirror the source, which layer is published to consumers, and who is accountable when the source schema changes underneath it.

## The short version Normalization and dimensional denormalization are not competing theories about the same problem. They are answers to two different questions: *how do I keep data correct while many writers change it a row at a time?* and *how do I let many readers scan enormous amounts of it and agree on the answer?* A schema that is excellent at one is usually mediocre at the other, which is why the same business data ends up in two shapes. ## What normalization is protecting in the source system The guarantee a normalized transactional schema gives you is that each non-key fact is stored once, in the table it belongs to. (The formal machinery behind that — the normal forms and functional dependencies — is the OLTP schema-design topic; here it only matters as the thing being traded away.) Because a customer's region name lives in exactly one row of one table, an application can rename that region with one `UPDATE`, and no other row in the database can disagree with it. Foreign keys, unique constraints and `NOT NULL` enforce that structure continuously, against dozens of independent code paths written by different teams over years, running concurrently. That guarantee is expensive on the read side and worth every penny on the write side. An order-entry system does thousands of small writes per second and its correctness is existential. ## Why the warehouse can spend that protection The analytics copy has a completely different writer profile: **one process, on a schedule, writing in bulk, deriving everything from a source of record it does not own.** Nothing else updates it. That changes the calculus in three ways: - Redundancy is no longer dangerous, because no ad-hoc statement can update one copy and miss another. The copies are all produced by the same transformation and can be re-derived by re-running it. - Constraints are less useful, because the load already knows the shape of what it writes; correctness is enforced by the transformation logic and by assertions run after it, not by the engine rejecting a bad row. - Write cost stops mattering. Nobody is trying to make a single-row update cheap in a table that is only ever rebuilt or merged in batches. ## What flattening actually buys the reader Consider a source where `customer -> city -> region -> country` is a four-table chain. Every question about sales by region requires an analyst to reconstruct that chain. In a dimensional model, `dim_customer` carries `city_name`, `region_name` and `country_name` as plain columns, so the same question is one join from the fact table. The gains compound: - **Fewer joins per query.** Join count is the dominant complexity in analytic SQL, and each one is a chance to fan out rows and inflate a `SUM`. - **Fewer chances to get the business logic wrong.** If every analyst re-derives "active customer" from three status columns, they will not all derive it identically. Flattening it into one modelled column makes the definition a property of the model rather than of whoever wrote the query. - **A learnable surface.** A star of a few tables with descriptive column names is something a business user can navigate; a 200-table normalized ERD is not. - **Stability.** The model is a published contract that survives source refactors, because the load absorbs the change. ## What it costs It is a trade, not a free win: - **Storage and load work.** The flattened attributes are computed and rewritten on every load. - **No engine-enforced integrity.** Nothing stops the load from writing a `region_name` that does not exist in the source. That is why grain-uniqueness and referential assertions become part of the pipeline. - **Change propagation is a job, not a statement.** Renaming a region is one `UPDATE` in the source and a re-derivation across many rows in the warehouse. - **Some history questions get harder or easier depending on choices you now have to make explicitly** — whether a changed attribute overwrites or creates a new version becomes a modelling decision rather than an accident. ## Where the line actually sits "Denormalized" is not a synonym for "one table". The consumption layer flattens; the staging and intermediate layers of an ELT pipeline usually stay close to the source shape on purpose, because a mechanical mirror of the source is easier to debug and re-extract. So the honest description is: the *presentation* layer is shaped for readers, and the layers behind it are shaped for the load. ## Answering this in an interview Say the two workloads out loud, name the guarantee normalization gives, say why one controlled writer makes that guarantee affordable to trade, then name the cost. Candidates who only say "warehouses denormalize because joins are slow" have half the answer; the other half is why it is *safe* to do so.

  • If denormalizing is such a win for readers, why not denormalize the transactional database too?
    Because the write path pays for it. In OLTP, a wider row with copied attributes means every small update rewrites more data and more index entries, holds locks longer, and — critically — has many independent writers who can update one copy and miss another. The warehouse escapes both problems by having one batch writer and no small updates.
  • What replaces the foreign keys and check constraints you gave up in the warehouse model?
    The load logic plus assertions run over the loaded tables: uniqueness on the declared grain, not-null on keys, accepted values on codes, and reconciliation of counts and sums against the source. The difference is timing — OLTP rejects a bad row at write time, analytics detects it after the load and fails the pipeline.
  • Does denormalizing mean the model contains no normalized tables at all?
    No. Staging and intermediate layers usually mirror the source shape deliberately, and relationships that are genuinely one-to-many stay in their own tables rather than being flattened into numbered columns. It is the presentation layer that consumers query which is shaped wide.

A transactional schema is a filing system for clerks who each edit one folder at a time; the analytics model is the printed report they all read from, reprinted whole whenever the folders change.

saying these in an interview costs you the question

  • Says denormalization is mainly about saving disk space
  • Claims normalization is outdated or wrong in general
  • Thinks warehouses denormalize because engines cannot do joins
  • Names only the benefits and no cost of denormalizing
  • Treats 3NF and dimensional modelling as rivals for the same workload

context