Third Normal Form   «Prev  Next»

Lesson 9Denormalization
ObjectiveExplain when denormalization is justified and how to maintain and verify a derived read structure.

Denormalizing Tables: Rationale, Example, and Tradeoffs

Denormalization deliberately stores duplicated or precomputed information to improve a specific access pattern. It can reduce repeated joins or aggregation work, but adds storage, write work, and consistency responsibilities. It is justified by measured workload benefits, not simply by the presence of joins.

The preceding lessons explained how normalization separates facts according to dependencies. Denormalization should begin with that understanding. Know which facts are authoritative, which copies are derived, and how disagreement will be detected and repaired.

Denormalization Does Not Always Mean Replacing the Base Tables

Combining relations or adding repeated descriptive attributes can reduce a table's normal form. However, a practical design can retain normalized base tables and add a separate reporting table, stored summary, or materialized result. The base relations do not lose their normal form merely because another structure duplicates their information.

An ordinary view can simplify a query interface without storing a second copy of the result. A materialized result stores data and therefore needs a maintenance policy. Product support and refresh behavior vary; do not assume that creating a view automatically caches its rows.

Measure the Problem Before Changing the Design

Identify the exact query, its frequency, the amount of data processed, and the response time users require. Inspect its execution plan and test representative data volumes. A query with several joins can be efficient; a query against one very wide table can still be expensive.

For example, a dashboard refreshed every few minutes might benefit from a stored summary. A small lookup queried occasionally may gain little from duplication. These are hypotheses to test rather than universal rules.

Example: A Genre Team Directory

A media company needs a frequently viewed list of employees, their genres, and the manager of each genre. Use these explicit rules:

The normalized source design is:

Employee(EmployeeID PK, EmployeeName, GenreID FK)
Genre(GenreID PK, GenreName, ManagerID FK)
Manager(ManagerID PK, ManagerName)

Employee.GenreID references Genre.GenreID
Genre.ManagerID references Manager.ManagerID

This is schema notation. The required foreign keys are non-null for the stated participation rules. The directory query is:

SELECT e.EmployeeID,
       e.EmployeeName,
       g.GenreID,
       g.GenreName,
       m.ManagerID,
       m.ManagerName
FROM Employee AS e
JOIN Genre AS g ON g.GenreID = e.GenreID
JOIN Manager AS m ON m.ManagerID = g.ManagerID;

Suppose profiling shows this access pattern remains expensive after suitable tuning, and users accept a directory that can lag behind changes by up to five minutes. A separate read table is one option:

GenreTeamDirectory(
  EmployeeID PK, EmployeeName, GenreID, GenreName,
  ManagerID, ManagerName
)

EmployeeID remains unique because the example permits one genre per employee. If employees could belong to multiple teams, the read table's grain and key would need to change. Always define what one row represents before building a reporting structure.

Populate the Derived Table

After creating an empty destination with appropriate column types, this illustrative SQL copies the current directory:

INSERT INTO GenreTeamDirectory
  (EmployeeID, EmployeeName, GenreID, GenreName,
   ManagerID, ManagerName)
SELECT e.EmployeeID,
       e.EmployeeName,
       g.GenreID,
       g.GenreName,
       m.ManagerID,
       m.ManagerName
FROM Employee AS e
JOIN Genre AS g ON g.GenreID = e.GenreID
JOIN Manager AS m ON m.ManagerID = g.ManagerID;

This is an initial population statement, not a complete refresh system. Running it repeatedly against the populated table would collide with existing EmployeeID keys. Production refresh logic must handle additions, changes, and deletions deliberately.

The read query can now use the stored result:

SELECT EmployeeID, EmployeeName, GenreName, ManagerName
FROM GenreTeamDirectory;

GenreName and ManagerName repeat for employees who share a team or manager. A manager rename no longer changes just one stored occurrence across the whole design: every affected directory copy must eventually reflect it. The potential reduction in read work has created additional maintenance work.

Choose a Consistency Policy

Possible maintenance approaches
ApproachBenefitResponsibility
Synchronous maintenance in the source transactionSource and derived changes commit together.Handle every write path; measure extra writes, locking, and transaction duration.
Asynchronous change processingMoves derived maintenance away from the immediate source write.Handle lag, ordering, retries, deletions, and duplicate event delivery.
Scheduled rebuild or refreshProvides a straightforward batch maintenance model.Budget refresh time and publish a complete, consistent result.

A trigger or stored procedure is an implementation mechanism, not a complete consistency policy by itself. Likewise, two separate application writes can leave mismatched data if the first succeeds and the second fails. Define the transaction or delivery guarantees that connect the writes.

For the directory, build a new result from a consistent source snapshot, validate it, and publish it using a mechanism supported by the chosen DBMS. Avoid exposing an empty or partially populated directory while rebuilding. Record the source snapshot time and successful publication time so the displayed freshness has a clear meaning.

A five-minute schedule alone does not guarantee data is at most five minutes old. Queue delays, rebuild duration, and failures also contribute. Monitor the actual age, define what readers should do if the limit is exceeded, and provide a way to rebuild from the authoritative tables.

Test More Than the Read Query

  1. Compare results: Match the derived rows to the normalized query at the same source snapshot. Check values and keys, not only row counts.
  2. Exercise changes: Add an employee, move an employee to another genre, rename a genre, and replace a manager.
  3. Check deletions: Confirm that removed employees disappear from the directory and no stale rows remain.
  4. Simulate failures: Interrupt a refresh or replay a change. Verify recovery without duplicates or partial publication.
  5. Measure total cost: Compare reads, writes, storage, refresh load, and operational effort against the baseline.

Keep the directory read-only to ordinary application users. Corrections should go through the source tables and the maintenance process. Deleting the last employee in a genre then removes a directory row without erasing the authoritative Genre record.

Distinguish Duplication from Historical Facts

An order line's agreed unit price is not necessarily a redundant copy of a product's current list price. The agreed price describes a transaction at a particular time; updating it whenever the catalog price changes would destroy history. Identify each attribute's meaning before classifying it as denormalization.

The same distinction applies to names and addresses saved on issued documents. Decide whether the stored value is a current display copy, an immutable historical fact, or something else. These choices require different maintenance rules.

When to Keep the Normalized Query

Prefer the simpler normalized design when it already meets the service target, when duplicated facts change frequently, or when the proposed refresh process costs more than it saves. Strict freshness requirements may rule out an asynchronous copy, although a carefully designed transactional alternative may still be possible.

Preserving normalized base tables also makes the optimization reversible: readers can return to the original query if the derived structure stops providing value. Review the decision after workload or infrastructure changes rather than treating redundancy as permanent.

Document the Decision

Record the target query, measured baseline, expected improvement, source tables, row grain, key, refresh mechanism, freshness limit, owner, validation checks, and rollback procedure. Specify which component is authoritative for each fact. A future maintainer should be able to distinguish a deliberate performance copy from accidental duplicate data.

Summary

Denormalization can improve selected reads by storing work in advance, but fewer joins do not guarantee better overall performance. Retain clear ownership of facts, design consistency and recovery before deployment, and verify the benefit under realistic workloads. Continue to the module conclusion to review normalization and its tradeoffs.

Denormalization: Deliberately storing redundant or precomputed information for an access requirement, with explicit rules for correctness and maintenance.

SEMrush Software 7 SEMrush Banner 7