Skip to content
Bible, Lee, Data
Engineering/Data

Why Does Database Normalization Matter?

Database normalization is often introduced through a short list of rules:

First Normal Form: atomic values
Second Normal Form: remove partial dependencies
Third Normal Form: remove transitive dependencies
BCNF: every determinant must be a candidate key

These definitions are useful when preparing for an exam.

On their own, however, they do not explain how to examine a real table, identify what is wrong with it, or decide where it should be split.

The more useful starting point is not the name of a normal form. It is a practical question:

What happens when the same fact is stored in several places?

Suppose a customer’s name is repeated in every order row. A product’s name and price are copied into every sales record. An employee’s department information appears in every task assigned to that employee.

At first, the design may look convenient. A single table can return everything without a join.

The problems appear when the data changes.

If a customer changes their name, every row containing that customer must be updated. If one row is missed, the database contains two different names for the same customer. A new product may be impossible to register before its first order. Deleting the final order for a product may accidentally remove the only remaining record of that product.

Normalization addresses these problems by separating different kinds of facts and storing each fact in the relation where it naturally belongs.

Its purpose is not simply to create more tables.

Normalization reduces uncontrolled duplication, protects consistency, and prevents the insertion, update, or deletion of one fact from unintentionally damaging another.

Once that purpose is clear, normal forms become easier to understand. They are not arbitrary rules. They are a sequence of techniques for removing specific structural problems.


Starting with one order table

Consider an online store that stores order data in a single table.

Order IDOrder DateCustomer IDCustomer NameCustomer TierProduct IDProduct NameUnit PriceQuantity

O1001 2026-07-01 C101 Minsoo Kim GOLD P10 Keyboard 50,000 1
O1001 2026-07-01 C101 Minsoo Kim GOLD P20 Mouse 30,000 2
O1002 2026-07-02 C102 Jieun Lee SILVER P10 Keyboard 50,000 1
O1003 2026-07-03 C101 Minsoo Kim GOLD P30 Monitor 300,000 1

Assume that the same product appears at most once in a given order.

A row can therefore be identified by the combination of order_id and product_id.

Primary key: (order_id, product_id)

The table contains several different kinds of facts:

  • Order O1001 was created on July 1, 2026.
  • Order O1001 belongs to customer C101.
  • Customer C101 is named Minsoo Kim.
  • Customer C101 belongs to the GOLD tier.
  • Product P10 is a keyboard.
  • Product P10 currently costs 50,000.
  • Order O1001 contains two units of product P20.

These facts are stored in the same row, but they do not describe the same thing.

The order date describes an order. The customer name describes a customer. The product name and price describe a product. The quantity describes the relationship between one order and one product.

Normalization begins by recognizing these distinctions.


Duplication matters because it creates anomalies

Not every repeated value is automatically a design error.

The real problem is that the same fact may be stored in several rows and then allowed to disagree with itself.

Normalization literature commonly groups these problems into three kinds of anomalies:

  • update anomalies
  • insertion anomalies
  • deletion anomalies

Update anomaly: one fact requires several updates

Suppose customer C101 changes their name from Minsoo Kim to Minsu Kim.

In the current table, the customer name appears in every order made by that customer.

Order IDCustomer IDCustomer Name

O1001 C101 Minsoo Kim
O1003 C101 Minsoo Kim

Every matching row must be updated.

UPDATE order_detail
SET customer_name = 'Minsu Kim'
WHERE customer_id = 'C101';

If the update condition is wrong or only some rows are changed, the database may end up with contradictory data.

Order IDCustomer IDCustomer Name

O1001 C101 Minsu Kim
O1003 C101 Minsoo Kim

The database can no longer answer a simple question confidently:

What is the name of customer C101?

The problem exists because one customer name was stored as several independent values.


Insertion anomaly: one fact cannot exist without another

Suppose the store wants to register a product before anyone orders it.

Product ID: P40
Product name: Webcam
Unit price: 80,000

If product information exists only inside the order table, the product cannot be inserted without also inventing:

  • an order ID
  • a customer ID
  • an order date
  • a quantity

The database would need either a fake order or several meaningless NULL values.

The same problem affects customers.

A newly registered customer who has not placed an order cannot be represented if customer information exists only as part of an order row.

The product and customer facts are incorrectly dependent on the existence of an order.


Deletion anomaly: removing one fact destroys another

Suppose product P30 appears only in order O1003.

O1003, C101, Minsoo Kim, P30, Monitor, 300,000, 1

If the order is canceled and the row is deleted, the database also loses its only record of product P30.

DELETE FROM order_detail
WHERE order_id = 'O1003'
  AND product_id = 'P30';

The intention was to remove an order item.

Instead, the operation also removed the product’s name and price.

This happens because one row represents several independent facts at once.


Functional dependency is the language of normalization

To split a table correctly, we need to identify which attributes determine other attributes.

This relationship is called a functional dependency.

If every value of an attribute set X is associated with exactly one value of an attribute set Y, we write:

X → Y

X is called the determinant, and Y is functionally dependent on X.

For the order table, the business rules suggest the following dependencies:

order_id → order_date, customer_id
customer_id → customer_name, customer_tier
product_id → product_name, unit_price
(order_id, product_id) → quantity

An order ID determines its date and customer.

O1001 → 2026-07-01, C101

A customer ID determines the customer’s current name and tier.

C101 → Minsoo Kim, GOLD

A product ID determines the product’s current name and price.

P10 → Keyboard, 50,000

Quantity requires both the order and the product.

(O1001, P20) → 2

Neither order_id alone nor product_id alone determines the quantity.

Identifying these dependencies is the central analytical step in normalization.


Functional dependencies come from business rules, not accidental data patterns

A functional dependency should not be inferred only from the rows currently stored in the table.

Suppose every customer in a small sample happens to belong to the GOLD tier.

Customer IDCustomer NameCustomer Tier

C101 Minsoo Kim GOLD
C102 Jieun Lee GOLD

The current rows might make this dependency appear true:

customer_tier → customer_name

But many customers can belong to the same tier. A tier does not uniquely determine a customer.

The following dependency is meaningful because it comes from the business model:

customer_id → customer_name, customer_tier

A customer ID identifies one customer.

Functional dependencies describe rules that the system is expected to preserve across all valid data, not coincidences in a small sample.


Keys must be understood before dependencies can be evaluated

Normalization relies heavily on several kinds of keys.

Superkey

A superkey is any set of attributes that can uniquely identify a row.

In an order item table, this set may identify a row:

(order_id, product_id)

This larger set also identifies a row:

(order_id, product_id, customer_id)

It is still a superkey, even though customer_id is unnecessary.

Candidate key

A candidate key is a minimal superkey.

It identifies a row, and no attribute can be removed without losing that ability.

(order_id, product_id)

Removing either order_id or product_id makes the set insufficient.

Primary key

A primary key is the candidate key chosen as the table’s main identifier.

Alternate key

An alternate key is a candidate key that was not chosen as the primary key.

Composite key

A composite key contains more than one attribute.

(order_id, product_id)

Composite keys become especially important when evaluating Second Normal Form.


First Normal Form: each field holds one value

Consider this order table:

Order IDCustomer IDOrdered ProductsQuantities

O1001 C101 P10, P20 1, 2
O1002 C102 P10 1

The ordered_products and quantities columns contain several values in one field.

Searching for orders containing P10 may require string matching.

SELECT *
FROM orders
WHERE ordered_products LIKE '%P10%';

The application must also interpret how the product positions correspond to the quantity positions.

First Normal Form requires each attribute value to be represented as a single value in the relation.

The order products should be represented as separate rows.

Order IDCustomer IDProduct IDQuantity

O1001 C101 P10 1
O1001 C101 P20 2
O1002 C102 P10 1

Now the database can query products directly.

SELECT *
FROM order_items
WHERE product_id = 'P10';

First Normal Form is often summarized as:

One field should contain one value.

The word atomic does not necessarily mean physically indivisible.

A date can be divided into year, month, and day, but storing an order date as one DATE value normally does not violate 1NF. The database treats the date as one meaningful value for the application.

A comma-separated list of product IDs is different. It combines multiple independently searchable and referential values into one field.


First Normal Form does not remove all redundancy

After separating multi-valued attributes into rows, the table may still look like this:

Order IDOrder DateCustomer IDCustomer NameCustomer TierProduct IDProduct NameUnit PriceQuantity

O1001 2026-07-01 C101 Minsoo Kim GOLD P10 Keyboard 50,000 1
O1001 2026-07-01 C101 Minsoo Kim GOLD P20 Mouse 30,000 2

The order date and customer information repeat for every product in the order.

Product information repeats across different orders.

First Normal Form removes repeating groups and multi-valued attributes. It does not remove attributes that depend on only part of a composite key.

That is the concern of Second Normal Form.


Second Normal Form: remove dependencies on part of a composite key

Assume that the primary key is:

(order_id, product_id)

Quantity depends on the complete key:

(order_id, product_id) → quantity

However, the order date and customer ID depend only on order_id.

order_id → order_date, customer_id

The product name and unit price depend only on product_id.

product_id → product_name, unit_price

These are partial functional dependencies because non-key attributes depend on only part of the composite key.

(order_id, product_id)
        ├── order_id → order_date, customer data
        └── product_id → product_name, unit_price

A relation is in Second Normal Form when:

  1. it is already in First Normal Form, and
  2. every non-prime attribute is fully dependent on every candidate key.

To remove the partial dependencies, split the table.

Orders

Order IDOrder DateCustomer IDCustomer NameCustomer Tier

O1001 2026-07-01 C101 Minsoo Kim GOLD
O1002 2026-07-02 C102 Jieun Lee SILVER
O1003 2026-07-03 C101 Minsoo Kim GOLD

Products

Product IDProduct NameUnit Price

P10 Keyboard 50,000
P20 Mouse 30,000
P30 Monitor 300,000

Order Items

Order IDProduct IDQuantity

O1001 P10 1
O1001 P20 2
O1002 P10 1
O1003 P30 1

A corresponding SQL design might begin like this:

CREATE TABLE orders (
    order_id VARCHAR(20) PRIMARY KEY,
    order_date DATE NOT NULL,
    customer_id VARCHAR(20) NOT NULL,
    customer_name VARCHAR(100) NOT NULL,
    customer_tier VARCHAR(20) NOT NULL
);

CREATE TABLE products (
    product_id VARCHAR(20) PRIMARY KEY,
    product_name VARCHAR(100) NOT NULL,
    unit_price BIGINT NOT NULL
);

CREATE TABLE order_items (
    order_id VARCHAR(20) NOT NULL,
    product_id VARCHAR(20) NOT NULL,
    quantity INT NOT NULL,
    PRIMARY KEY (order_id, product_id)
);

Product names and prices now appear once in the product table.

Adding another product to an order no longer duplicates the order date and product-independent order data.


A single-attribute candidate key automatically avoids partial dependency

A partial dependency requires a composite candidate key.

If a candidate key contains only one attribute, there is no smaller part of that key on which an attribute can depend.

For example:

product_id → product_name, unit_price

Because product_id is a single-attribute key, this dependency is already full.

A relation that is in 1NF and has only single-attribute candidate keys therefore satisfies 2NF automatically.

However, adding a surrogate key does not necessarily fix the underlying design.


A surrogate key does not automatically normalize a table

Suppose the original order item table receives an auto-incrementing order_item_id.

Order Item IDOrder IDCustomer IDCustomer NameProduct IDProduct NameQuantity

1 O1001 C101 Minsoo Kim P10 Keyboard 1
2 O1001 C101 Minsoo Kim P20 Mouse 2

The primary key is now a single column:

order_item_id

It may appear that the composite-key problem has disappeared.

The business dependencies, however, still exist:

order_id → customer_id
customer_id → customer_name
product_id → product_name

Customer names and product names remain duplicated. The update anomalies remain.

A surrogate key is useful for identifying a row. It does not remove the natural dependencies among the business attributes.

Normalization should therefore consider all candidate keys and meaningful dependencies, not only the declared primary key.


Third Normal Form: remove transitive dependencies

After the 2NF decomposition, consider the order table again.

Order IDOrder DateCustomer IDCustomer NameCustomer Tier

O1001 2026-07-01 C101 Minsoo Kim GOLD
O1002 2026-07-02 C102 Jieun Lee SILVER
O1003 2026-07-03 C101 Minsoo Kim GOLD

The primary key is order_id.

order_id → order_date, customer_id, customer_name, customer_tier

But the customer name and tier are not directly facts about the order.

They are determined by customer_id.

order_id → customer_id
customer_id → customer_name, customer_tier

This creates an indirect dependency:

order_id → customer_id → customer_name, customer_tier

This is a transitive functional dependency.

Third Normal Form removes this kind of dependency among non-key attributes.

Move the customer attributes into their own relation.

Orders

Order IDOrder DateCustomer ID

O1001 2026-07-01 C101
O1002 2026-07-02 C102
O1003 2026-07-03 C101

Customers

Customer IDCustomer NameCustomer Tier

C101 Minsoo Kim GOLD
C102 Jieun Lee SILVER

The SQL structure becomes:

CREATE TABLE customers (
    customer_id VARCHAR(20) PRIMARY KEY,
    customer_name VARCHAR(100) NOT NULL,
    customer_tier VARCHAR(20) NOT NULL
);

CREATE TABLE orders (
    order_id VARCHAR(20) PRIMARY KEY,
    order_date DATE NOT NULL,
    customer_id VARCHAR(20) NOT NULL,
    CONSTRAINT fk_orders_customer
        FOREIGN KEY (customer_id)
        REFERENCES customers (customer_id)
);

CREATE TABLE products (
    product_id VARCHAR(20) PRIMARY KEY,
    product_name VARCHAR(100) NOT NULL,
    unit_price BIGINT NOT NULL
);

CREATE TABLE order_items (
    order_id VARCHAR(20) NOT NULL,
    product_id VARCHAR(20) NOT NULL,
    quantity INT NOT NULL,
    PRIMARY KEY (order_id, product_id),
    CONSTRAINT fk_order_items_order
        FOREIGN KEY (order_id)
        REFERENCES orders (order_id),
    CONSTRAINT fk_order_items_product
        FOREIGN KEY (product_id)
        REFERENCES products (product_id)
);

Each table now represents a clearer category of fact.

customers
facts about customers

orders
facts about orders

products
facts about products

order_items
facts about the relationship between orders and products

A customer name is updated once.

A customer can exist before placing an order.

Deleting the customer’s last order does not remove the customer or product records.


A more precise definition of Third Normal Form

Third Normal Form is often summarized as:

Remove transitive dependencies.

This is a helpful practical rule, but the formal condition is slightly more precise.

For every non-trivial functional dependency:

X → A

at least one of the following must hold:

  • X is a superkey.
  • A is a prime attribute, meaning it belongs to at least one candidate key.

In many ordinary application tables, the following intuition is sufficient:

If a non-key attribute determines another non-key attribute, consider separating them.

When several overlapping candidate keys exist, however, the distinction between prime and non-prime attributes becomes important.


BCNF: every determinant must be a superkey

Boyce-Codd Normal Form is stricter than Third Normal Form.

Its central requirement is:

For every non-trivial functional dependency, the determinant must be a superkey.

If X → Y holds, X must be a superkey.

Many simple tables that satisfy 3NF also satisfy BCNF.

The difference becomes visible when a relation has multiple overlapping candidate keys.


A relation that is in 3NF but not BCNF

Consider a teaching assignment relation.

Student IDSubject CodeProfessor ID

S101 DB P10
S102 DB P10
S101 OS P20
S103 OS P20

Assume the following rules:

  1. For a given subject, each student is assigned to one professor.
  2. Each professor teaches exactly one subject.

The functional dependencies are:

(student_id, subject_code) → professor_id
professor_id → subject_code

The candidate keys are:

(student_id, subject_code)
(student_id, professor_id)

All three attributes are prime attributes because each belongs to at least one candidate key.

Now examine:

professor_id → subject_code

professor_id is not a superkey because many students can be assigned to the same professor.

The dependency therefore violates BCNF.

The relation may still satisfy 3NF because subject_code, the dependent attribute, is prime.

To reach BCNF, decompose the relation.

Professor Subjects

Professor IDSubject Code

P10 DB
P20 OS

Student Professors

Student IDProfessor ID

S101 P10
S102 P10
S101 P20
S103 P20
CREATE TABLE professor_subjects (
    professor_id VARCHAR(20) PRIMARY KEY,
    subject_code VARCHAR(20) NOT NULL
);

CREATE TABLE student_professors (
    student_id VARCHAR(20) NOT NULL,
    professor_id VARCHAR(20) NOT NULL,
    PRIMARY KEY (student_id, professor_id),
    FOREIGN KEY (professor_id)
        REFERENCES professor_subjects (professor_id)
);

The fact that a professor teaches one subject is now stored once.


Normalization does not mean splitting every attribute into a separate table

Normalization can be misunderstood as a rule that every column should eventually be moved into its own relation.

That is not the goal.

Consider:

Customer IDCustomer NameEmail

C101 Minsoo Kim minsoo@example.com
C102 Jieun Lee jieun@example.com

The dependency is:

customer_id → customer_name, email

Both attributes describe the customer and depend directly on the customer key.

There is no normalization benefit in creating separate customer_names and customer_emails tables simply because the values are different.

Normalization is not about minimizing the number of columns in a table.

It is about detecting when distinct facts are mixed together in a way that creates harmful dependencies and anomalies.


A decomposition must preserve the original information

After splitting a relation, the original information must still be recoverable.

For example, orders and customers should be joinable to reproduce customer details for an order.

SELECT
    o.order_id,
    o.order_date,
    c.customer_id,
    c.customer_name,
    c.customer_tier
FROM orders o
JOIN customers c
  ON c.customer_id = o.customer_id;

A decomposition that allows the original relation to be reconstructed without losing information or inventing new facts is called a lossless decomposition.

Improper decomposition can produce spurious tuples—combinations that were never present in the original data.

The goal is not to scatter attributes across many tables. It is to separate facts while preserving their correct relationships.


Dependency preservation is another important property

Before decomposition, a functional dependency may be enforceable inside one table.

After decomposition, important business rules should ideally remain enforceable without requiring expensive joins across several relations.

For example:

customer_id → customer_name

is easy to preserve when both attributes remain in the customers table.

A primary key or unique constraint can ensure that one customer ID corresponds to one customer row.

A good decomposition usually considers both:

  • Lossless join: the original information can be reconstructed correctly.
  • Dependency preservation: important functional dependencies remain enforceable within the decomposed relations.

It is not always possible to satisfy both properties perfectly at every higher normal form.

BCNF decomposition, for example, may sometimes sacrifice dependency preservation. In such cases, the design must balance formal normalization, enforceability, and operational complexity.


Does normalization make queries slower?

A normalized design often requires joins.

To display an order with customer and product information, an application may query several tables.

SELECT
    o.order_id,
    o.order_date,
    c.customer_name,
    p.product_name,
    p.unit_price,
    oi.quantity
FROM orders o
JOIN customers c
  ON c.customer_id = o.customer_id
JOIN order_items oi
  ON oi.order_id = o.order_id
JOIN products p
  ON p.product_id = oi.product_id
WHERE o.order_id = 'O1001';

This sometimes leads to the claim that normalization makes databases slow.

A join is not automatically slow.

Query performance depends on factors such as:

  • data volume
  • indexes
  • selectivity
  • join order
  • table statistics
  • execution plans
  • memory and storage behavior
  • read and write frequency
  • database engine implementation

With suitable primary keys, foreign-key indexes, and a sensible query plan, normalized joins can be efficient.

A single wide table can create its own performance problems:

  • larger rows
  • repeated data
  • more storage and memory traffic
  • more expensive updates
  • larger indexes
  • broader locking impact
  • greater risk of inconsistent writes

Normalization and performance are not simple opposites.

Normalization establishes a correct logical model. Performance should then be evaluated through measurements and execution plans.


Denormalization is not the same as skipping normalization

Denormalization intentionally introduces selected duplication or precomputed data for a clear operational reason.

Suppose the product name and price may change, but an order must preserve what the customer actually purchased at the time of checkout.

The order item may store:

ordered_product_name
ordered_unit_price
CREATE TABLE order_items (
    order_id VARCHAR(20) NOT NULL,
    product_id VARCHAR(20) NOT NULL,
    ordered_product_name VARCHAR(100) NOT NULL,
    ordered_unit_price BIGINT NOT NULL,
    quantity INT NOT NULL,
    PRIMARY KEY (order_id, product_id)
);

The current product price and the order-time price are not the same fact.

products.unit_price
the current selling price

order_items.ordered_unit_price
the price agreed upon when the order was placed

The values may initially be equal, but their meanings and change rules differ.

This is not accidental duplication. It is a historical snapshot.

Other denormalization techniques include:

  • storing frequently used aggregates
  • maintaining summary tables
  • building read-optimized projections
  • duplicating selected attributes
  • merging tables for a measured workload
  • using materialized views
  • maintaining separate read models

A deliberate denormalization decision should answer questions such as:

  • What measured problem does this duplication solve?
  • Which data source is authoritative?
  • When and how is the duplicate updated?
  • What happens when synchronization fails?
  • How is inconsistency detected and repaired?

An unstructured table with uncontrolled repetition is not denormalized merely because someone calls it that.

Denormalization should be a conscious decision made after the logical model is understood.


Not all repeated values represent the same fact

Consider these values:

Current product price: 50,000
Order-time price: 50,000
Refund calculation amount: 50,000

The numbers are equal, but they represent different business facts.

If the current product price changes to 60,000, the historical order price should not change. The refund calculation may follow yet another rule.

Duplicating one current customer name across every order row is different. Those copies are expected to change together and represent the same fact.

Whether repetition is harmful depends on meaning, not on whether the textual values happen to match.


Time changes the meaning of dependencies

Suppose the customer table stores the customer’s current tier:

customers.customer_tier = GOLD

This is suitable when the application needs the current status.

But suppose a discount audit must reproduce the tier used when an order was placed.

A customer may have been SILVER at order time and upgraded to GOLD later. Joining the historical order to the current customer row would show the wrong historical tier.

The design may therefore require one of the following:

  • store the tier used for the order
  • keep a customer tier history table
  • store the calculated discount result
  • use effective start and end timestamps
  • model tier changes as business events

This does not necessarily mean abandoning normalization.

The current customer tier and the tier applied to a historical order are separate facts with different time semantics.

A good data model asks not only:

What determines this value?

It also asks:

At what point in time is this fact valid?


A practical sequence for solving normalization problems

When analyzing a normalization problem, the following sequence is useful.

1. Identify the attributes and business rules

Do not rely only on the sample rows. Determine what the problem statement says must be true.

2. Find all candidate keys

Identify minimal attribute sets that uniquely determine a row.

There may be more than one.

3. Write the functional dependencies

For example:

A → B
(A, C) → D
B → E

4. Check First Normal Form

Look for repeating groups, lists, arrays, or several independently meaningful values stored in one field.

5. Find partial dependencies

If a candidate key is composite, ask whether one part determines a non-prime attribute.

If (A, B) is a candidate key, does A → C hold?

If so, the relation may violate 2NF.

6. Find transitive dependencies

Ask whether a non-key attribute determines another non-key attribute.

key → A
A → B

If so, consider a 3NF decomposition.

7. Check every determinant

If a determinant is not a superkey, the relation may violate BCNF.

8. Validate the decomposition

Confirm that the decomposition is lossless and evaluate whether important dependencies remain enforceable.


Normal forms in one sentence each

First Normal Form

Each attribute should contain one relational value.

Remove repeating groups and multi-valued fields.

Second Normal Form

Every non-prime attribute must depend on the whole candidate key, not only part of it.

Remove partial dependencies.

Third Normal Form

Non-key attributes should not determine other non-key attributes through a transitive chain.

Remove transitive dependencies.

Boyce-Codd Normal Form

Every determinant must be a superkey.

Apply a stricter requirement than 3NF to relations with more complex candidate-key structures.

A common exam-oriented summary is:

1NF: atomic values
2NF: remove partial dependencies
3NF: remove transitive dependencies
BCNF: every determinant is a candidate key

The more precise BCNF wording is “every determinant must be a superkey,” because a determinant may contain additional attributes beyond a minimal candidate key.


In the final model, each table owns one kind of fact

After decomposing the order example, the main dependencies are:

customers
customer_id → customer_name, customer_tier

orders
order_id → order_date, customer_id

products
product_id → product_name, unit_price

order_items
(order_id, product_id) → quantity

The relationships are:

customers 1 ─── N orders
orders    1 ─── N order_items
products  1 ─── N order_items

Each table now has a clearer responsibility.

  • A customer row describes one customer.
  • An order row describes one order.
  • A product row describes one product.
  • An order item row describes how one product participates in one order.

Updating one kind of fact is less likely to damage another.


Normalization determines where each fact is authoritative

Normalization is not only about reducing storage.

In modern systems, more important questions often include:

  • Which value is the source of truth?
  • If two copies disagree, which one should be trusted?
  • Which rows must change when one business fact changes?
  • What information should remain after a related record is deleted?
  • Can an entity exist before a relationship to another entity is created?

If a customer name is copied into every order row, the database has no obvious authoritative value.

If the current customer name exists once in customers, its source is clear.

Authoritative current customer name
→ customers

If the current product price and historical order price represent separate facts, each also needs an explicit source.

Current product price
→ products

Order-time agreed price
→ order_items

A good data model does more than avoid duplicate strings and numbers.

It makes clear what every value means and where the authoritative version of that fact lives.


The goal of normalization is consistency, not table count

A normalized database does not need to have as many tables as possible.

A database with only a few tables is not necessarily denormalized.

The relevant questions are about facts and dependencies, not table count.

Normalization helps reduce:

  • updating the same fact in several places
  • inconsistent copies of one fact
  • inability to insert an independent fact
  • accidental deletion of unrelated information
  • ambiguity about which value is authoritative

First Normal Form addresses repeated groups and multi-valued fields.

Second Normal Form removes dependencies on part of a composite key.

Third Normal Form removes indirect dependencies among non-key attributes.

BCNF applies the stricter rule that every determinant must identify a row.

Higher normalization is not an end in itself.

The decomposition should remain lossless, important dependencies should remain enforceable where possible, and real query and update patterns must be considered. Historical snapshots and measured performance requirements may justify deliberate duplication.

The important distinction is whether duplication is accidental or intentional.

To understand normalization properly, it is not enough to memorize the definitions of 1NF, 2NF, 3NF, and BCNF.

When examining a table, we should be able to ask:

What fact does this row represent?
What determines each attribute?
Can the same fact change independently in several places?
Will inserting or deleting one fact damage another?
Where is the authoritative version of this value?

Normalization is the process of answering those questions and assigning each fact to an appropriate relation.

Ultimately, normalization is not merely a technique for splitting tables.

It is a method for distributing responsibility inside a database so that each fact can be stored, changed, and trusted consistently.

Isaac S. Lee
Faith, software, data, and everyday life.