Skip to content
Database Normalization and Denormalization: Organizing Data for Performance 5 min

Database Normalization and Denormalization: Organizing Data for Performance

SD
ScaleDojo
May 11, 2026
5 min read1,287 words
1
Normalization and Denormalization: Organizing Data for Performance

The $10 Million Bug

Getting database normalization and denormalization wrong can cost millions.A fintech startup stored all customer data in one flat table: order_id, customer_name, customer_email, customer_address, product_name, product_price. It worked great at 1,000 rows. At 50 million rows, a customer changed their address and 47,000 order rows needed updating. The migration script crashed halfway. Half the orders showed the old address, half showed the new one. Tax calculations were wrong. Refund addresses were wrong. This is exactly the problem normalization prevents.

The Problem: Data Duplication

BEFORE normalization (flat table):

orders_flat:
+----+--------+-----------+----------------+------------+-------+
| id | c_name | c_email   | c_address      | product    | price |
+----+--------+-----------+----------------+------------+-------+
| 1  | Alice  | a@mail.co | 123 Oak St     | Widget A   | 29.99 |
| 2  | Alice  | a@mail.co | 123 Oak St     | Gadget B   | 49.99 |
| 3  | Alice  | a@mail.co | 123 Oak St     | Widget A   | 29.99 |
| 4  | Bob    | b@mail.co | 456 Pine Ave   | Gadget B   | 49.99 |
| 5  | Alice  | a@mail.co | 123 Oak St     | Gizmo C    | 19.99 |
+----+--------+-----------+----------------+------------+-------+

Problems:
  - Alice's info is duplicated 4 times
  - Address change requires updating ALL 4 rows
  - If we miss one row, data is inconsistent
  - Storage wasted on repeated strings


AFTER normalization (3NF):

customers:                    products:
+----+-------+-----------+   +----+----------+-------+
| id | name  | email     |   | id | name     | price |
+----+-------+-----------+   +----+----------+-------+
| 1  | Alice | a@mail.co |   | 1  | Widget A | 29.99 |
| 2  | Bob   | b@mail.co |   | 2  | Gadget B | 49.99 |
+----+-------+-----------+   | 3  | Gizmo C  | 19.99 |
                              +----+----------+-------+
orders:
+----+-------------+------------+
| id | customer_id | product_id |
+----+-------------+------------+
| 1  |      1      |     1      |
| 2  |      1      |     2      |
| 3  |      1      |     1      |
| 4  |      2      |     2      |
| 5  |      1      |     3      |
+----+-------------+------------+

Alice's address change = 1 update in 1 table. Done.

The Normal Forms: A Practical Guide

The normal forms are progressive rules. Each one eliminates a specific category of data anomaly:

Normalization: Designing Data Without Redundancy

Normalization removes duplicate data. If the same data appears in two places and you update one, the other becomes wrong. That is called data anomaly and it is one of the most painful bugs to track down in production.

1. First Normal Form (1NF)

For a table to be in 1NF, it must meet the following criteria:

  • Atomic values: Each column must contain only single, indivisible values (no arrays, comma-separated lists, or multiple values in a single cell).

  • Unique column names: Each attribute must have a unique name.

  • Order doesn't matter: The order of rows or columns shouldn't affect the data meaning.

  • Primary Key: The table must have a primary key to uniquely identify each row.


2. Second Normal Form (2NF)

To achieve 2NF, a table must:

  • Be in 1NF.

  • Have no partial dependencies. This means every non-prime attribute (a column that isn't part of the primary key) must be fully dependent on the entire primary key.

This rule only applies when you have a composite primary key (a key made of multiple columns). If a non-key column depends on only part of that composite key, it must be moved to a separate table.


3. Third Normal Form (3NF)

To achieve 3NF, a table must:

  • Be in 2NF.

  • Have no transitive dependencies. This means a non-prime attribute cannot depend on another non-prime attribute. Every non-key column must depend only on the primary key.


4. Boyce-Codd Normal Form (BCNF)

BCNF is a stricter, stronger version of 3NF, often called 3.5NF. A table is in BCNF if:

  • It is in 3NF.

  • For every functional dependency $X \rightarrow Y$, $X$ must be a super key.

In plain terms, an anomaly can still occur in 3NF if a table has multiple overlapping candidate keys. BCNF ensures that a non-key attribute cannot determine any part of a candidate key.


5. Fourth Normal Form (4NF)

To achieve 4NF, a table must:

  • Be in BCNF.

  • Have no multi-valued dependencies.

A multi-valued dependency occurs when the presence of one or more rows in a table implies the presence of one or more other rows. It happens when a single primary key independent tracker points to two or more independent multi-valued relationships.


6. Fifth Normal Form (5NF) / Project-Join Normal Form (PJNF)

To achieve 5NF, a table must:

  • Be in 4NF.

  • Not be able to be decomposed into smaller tables without losing data or introducing redundancy when those tables are joined back together (lossless join).Denormalization: Trading Integrity for Speed

Normalization makes writes safe but makes reads expensive. To display an order with customer info and product details, you need to JOIN 3-4 tables. Each join has a cost, especially at millions of rows.


Denormalization: Trading Integrity for Speed

Denormalization means intentionally duplicating data to eliminate joins and speed up reads. You KNOW the data is duplicated. You accept the maintenance cost because the read performance benefit is worth it.

Normalized query (3 JOINs):
  SELECT o.id, c.name, p.name, p.price
  FROM orders o
  JOIN customers c ON o.customer_id = c.id
  JOIN products p ON o.product_id = p.id
  WHERE o.created_at > '2024-01-01'
  -- Time: ~45ms on 10M orders (3 index lookups per row)

Denormalized query (no JOINs):
  SELECT id, customer_name, product_name, price_at_purchase
  FROM orders_denormalized
  WHERE created_at > '2024-01-01'
  -- Time: ~8ms on 10M orders (single table scan with index)

The denormalized table stores copies of customer_name,
product_name, and price_at_purchase directly on each order row.

Common Denormalization Patterns

1. Materialized Views

A Materialized View stores the result of a complex query as a physical table. Instead of executing expensive JOINs every time, the database periodically refreshes the stored data.

Best For: Dashboards, reports, analytics

Example: Precomputing monthly sales instead of calculating them on every request.


2. Computed Columns (Precomputed Aggregates)

Frequently used values like total_orders or average_rating are stored directly in a table and updated automatically using triggers or application logic.

Best For: Order counts, ratings, inventory totals

Example: Store total_orders = 243 in the Customers table instead of running COUNT(*) for every request.


3. Point-in-Time Snapshots

Store historical data directly in a record so it never changes, even if the original data changes later.

Best For: Orders, invoices, financial records

Example: Save the product name and price at purchase in the Orders table, ensuring old orders always display the original price.


4. Read Replicas & Search Indexes

Keep a denormalized copy of your data in systems like Elasticsearch, synchronized using Change Data Capture (CDC). This provides fast searching without affecting the primary database.

Best For: Search, recommendations, analytics, large-scale read workloads

Example: An e-commerce site stores complete product documents in Elasticsearch for lightning-fast product searches.

Decision Framework

Question                           Normalize    Denormalize
---------------------------------  ----------   -----------
Who writes more than reads?        YES          no
Data integrity is critical?        YES          no
Relationships change often?        YES          no
Read latency must be minimal?      no           YES
Reads vastly outnumber writes?     no           YES
Data rarely changes after write?   no           YES

Most real systems: normalize the source of truth,
then denormalize into read-optimized views/caches.

Normalization vs Denormalization

Feature

Normalization

Denormalization

Data Duplication

Minimal

Intentional

Read Performance

Moderate

Excellent

Write Performance

Excellent

Slower

Storage Usage

Lower

Higher

Data Integrity

Strong

More Complex

Maintenance

Easier

Harder

JOIN Operations

More

Fewer

Modern Architecture: The Best of Both Worlds

Most large-scale applications use both normalization and denormalization.

                 Client
                   │
                   ▼
               REST API
                  │
        ┌──────────┴──────────┐
        ▼                     ▼
 Write Database         Read Database
 (Normalized)          (Denormalized)

 PostgreSQL             Redis
 MySQL                  Elasticsearch
                         Materialized Views

The normalized database remains the source of truth.

Denormalized systems provide lightning-fast reads.


Normalization & Denormalization

Interview Tip

When an interviewer asks about schema design, say: 'I would start fully normalized in 3NF for the write path to ensure data integrity. Then I would identify the most common read queries and create targeted denormalizations - either as materialized views, cached aggregates, or a search index. The source of truth stays normalized; the read path is denormalized.' This shows you understand both sides and know when to apply each.

Key Takeaway

Normalization eliminates data duplication and maintains integrity. Denormalization copies data to speed up reads at the cost of more complex updates. The best architectures normalize for writes and denormalize for reads.

Enjoyed this article?

Share it with your network to help others level up their system design skills.

Discussion0

Join the Discussion

Sign in to leave comments, reply to others, or like insights.

Sign In to ScaleDojo

No comments yet. Be the first to start the thread!

Related Articles

Enjoyed this content?

Your support keeps us creating free resources

We put a lot of hours into researching and writing these guides. If it helped you, consider buying us a coffee. Every bit goes toward keeping ScaleDojo's content free and growing.

$

One-time payment via Stripe. ScaleDojo account required.

ScaleDojo Logo
Initializing ScaleDojo