| |

SQL 78 🛢️ Database Normalization: 1NF, 2NF, and 3NF

⏱️ Estimated read: 21 min | |

Normalization is the process of organizing a database’s columns and tables to reduce redundancy and eliminate the anomalies that redundancy causes. The theory is older than most of the databases that use it, but the practice is as relevant as ever: a table that stores a customer’s address on every order will eventually have two orders with two different addresses for the same customer. Normalization defines a series of normal forms, each stricter than the last, that describe how a table should be structured to avoid a specific class of problem. The first three forms — 1NF, 2NF, and 3NF — are the ones that appear in practice. Most production schemas are in 3NF, and the forms beyond it are used selectively.

Key point: Normalization is about eliminating redundancy, not about making the schema pretty. Each normal form fixes a specific kind of anomaly: insertion, update, and deletion. The forms are applied in order, and each one depends on the previous. A table cannot be in 2NF unless it is already in 1NF.


Why normalization exists

The redundancy problem. Storing the same fact in multiple places means the fact can become inconsistent. If a customer’s address is stored on every order, updating the customer’s address requires updating every order, and a missed order leaves the database with two different addresses for the same customer.

The insertion anomaly. A table that combines customer and order data cannot record a customer who has not placed an order, because the primary key requires an order. The customer information is unavailable until an order exists.

The update anomaly. Changing a customer’s phone number requires updating every row where the customer appears. If one row is missed, the table contains contradictory data, and no query can determine which value is correct.

The deletion anomaly. Deleting an order may delete the only record of a customer, which loses the customer information as a side effect. The data that should have been preserved is destroyed.

The multi-value problem. A column that holds a comma-separated list of values, such as a list of phone numbers, cannot be queried efficiently. Finding all customers with a specific phone number requires string matching, which cannot use an index.

The partial-dependency problem. In a table with a composite primary key, a non-key column that depends on only part of the key is a partial dependency. The value must be repeated for every combination of the key that shares that part, which is redundant.

The transitive-dependency problem. A non-key column that depends on another non-key column is a transitive dependency. The value is determined by a column that is not the primary key, which means it is redundant with respect to the key.


a. First Normal Form (1NF)

A table is in 1NF when every column contains a single atomic value, every row is unique, and no column contains repeating groups. The rule is simple: one value per cell.

The repeating-group violation

A table that stores a list of values in a single column violates 1NF.

-- Violates 1NF
CREATE TABLE customers (
    id      INT PRIMARY KEY,
    name    VARCHAR(100),
    phones  VARCHAR(200)  -- "555-1234, 555-5678"
);

The phones column holds multiple values. Finding a customer by phone number requires string matching, and updating one phone number requires parsing the string.

The 1NF solution

The solution is to move the multi-valued attribute into its own table, with one row per value.

-- 1NF compliant
CREATE TABLE customers (
    id    INT PRIMARY KEY,
    name  VARCHAR(100)
);

CREATE TABLE customer_phones (
    customer_id  INT,
    phone        VARCHAR(20),
    PRIMARY KEY (customer_id, phone),
    FOREIGN KEY (customer_id) REFERENCES customers(id)
);

Each phone is its own row. The primary key is the combination of customer_id and phone, so a customer cannot have the same phone twice.

The repeating-column violation

A table with columns like phone1, phone2, phone3 also violates 1NF, because the columns form a repeating group.

-- Violates 1NF
CREATE TABLE customers (
    id      INT PRIMARY KEY,
    name    VARCHAR(100),
    phone1  VARCHAR(20),
    phone2  VARCHAR(20),
    phone3  VARCHAR(20)
);

The table can only hold three phones. A fourth phone requires a schema change. The solution is the same: a separate table with one row per phone.

The atomic-value rule

The rule is that each cell holds a single value. A cell that holds a JSON document, an array, or a comma-separated list violates the rule, even though the database can store the value. The violation is not about the storage format; it is about whether the cell holds one fact or many.

-- Violates 1NF if the JSON holds multiple values.
CREATE TABLE orders (
    id     INT PRIMARY KEY,
    items  JSON  -- [{"sku": "A", "qty": 2}, {"sku": "B", "qty": 1}]
);

The JSON column can be indexed in some databases, and it can be queried, but the values inside it are not atomic with respect to the relational model. Whether this matters depends on the use case: a document store pattern can be appropriate when the JSON is treated as an opaque document, but it is a violation of 1NF when the values inside it are queried as relational data.

The unique-row rule

A table must have a primary key, and the key must uniquely identify each row. A table without a primary key is not in 1NF, because there is no way to distinguish two identical rows.

-- Violates 1NF: no primary key
CREATE TABLE logs (
    message  TEXT,
    level    VARCHAR(10)
);

Without a primary key, two identical log messages cannot be distinguished. The solution is to add a surrogate key.

CREATE TABLE logs (
    id       BIGSERIAL PRIMARY KEY,
    message  TEXT,
    level    VARCHAR(10)
);

b. Second Normal Form (2NF)

A table is in 2NF when it is in 1NF and every non-key column depends on the entire primary key, not just part of it. The rule applies to tables with composite primary keys. A table with a single-column primary key is automatically in 2NF if it is in 1NF.

The partial-dependency violation

A table that stores order line items with a composite key of (order_id, product_id) and a product_name column has a partial dependency: product_name depends on product_id alone, not on the full key.

-- Violates 2NF
CREATE TABLE order_items (
    order_id      INT,
    product_id    INT,
    product_name  VARCHAR(100),  -- Depends on product_id alone
    quantity      INT,
    PRIMARY KEY (order_id, product_id)
);

The product_name is repeated for every order that contains the product. If the product is renamed, every row must be updated, and a missed row leaves the database inconsistent.

The 2NF solution

The solution is to move the partially-dependent column to its own table.

-- 2NF compliant
CREATE TABLE order_items (
    order_id    INT,
    product_id  INT,
    quantity    INT,
    PRIMARY KEY (order_id, product_id),
    FOREIGN KEY (product_id) REFERENCES products(id)
);

CREATE TABLE products (
    id    INT PRIMARY KEY,
    name  VARCHAR(100)
);

The product_name is stored once in the products table. The order_items table stores only the quantity, which depends on the full key.

The single-key exception

A table with a single-column primary key does not have partial dependencies, because there is no “part” of the key. The table is automatically in 2NF if it is in 1NF.

-- 2NF compliant: single-column key
CREATE TABLE orders (
    id           INT PRIMARY KEY,
    customer_id  INT,
    order_date   DATE,
    total        DECIMAL(10,2)
);

Every non-key column depends on id, which is the entire key. There is no partial dependency.

The candidate key

A table can have more than one candidate key. A non-key column must depend on every candidate key, not just the primary one. A table where a column depends on one candidate key but not another violates 2NF.

-- Violates 2NF
CREATE TABLE employee_projects (
    employee_id  INT,
    project_id   INT,
    employee_name VARCHAR(100),  -- Depends on employee_id alone
    PRIMARY KEY (employee_id, project_id),
    UNIQUE (project_id, employee_id)
);

The employee_name depends on employee_id, which is part of both candidate keys. The violation is the same: the name is repeated for every project the employee works on.

The 2NF test

The test for 2NF is: for each non-key column, does it depend on the entire primary key? If the answer is no for any column, the table is not in 2NF.

Table: order_items
Key: (order_id, product_id)

order_id     → depends on the full key (both parts)
product_id   → depends on the full key (both parts)
quantity     → depends on the full key (the quantity is per order-product)
product_name → depends on product_id alone → VIOLATION

c. Third Normal Form (3NF)

A table is in 3NF when it is in 2NF and no non-key column depends on another non-key column. The rule eliminates transitive dependencies: a column that is determined by a column other than the key.

The transitive-dependency violation

A table that stores employees with a department_id and a department_name has a transitive dependency: department_name depends on department_id, which is not the key.

-- Violates 3NF
CREATE TABLE employees (
    id               INT PRIMARY KEY,
    name             VARCHAR(100),
    department_id    INT,
    department_name  VARCHAR(100)  -- Depends on department_id
);

The department_name is repeated for every employee in the department. If the department is renamed, every employee row must be updated.

The 3NF solution

The solution is to move the transitively-dependent column to its own table.

-- 3NF compliant
CREATE TABLE employees (
    id             INT PRIMARY KEY,
    name           VARCHAR(100),
    department_id  INT,
    FOREIGN KEY (department_id) REFERENCES departments(id)
);

CREATE TABLE departments (
    id    INT PRIMARY KEY,
    name  VARCHAR(100)
);

The department_name is stored once in the departments table. The employees table stores only the department_id.

The dependency chain

The transitive dependency is a chain: id → department_id → department_name. The key determines department_id, and department_id determines department_name. The chain means department_name is determined by the key transitively, but the rule requires that every non-key column depend on the key directly.

The 3NF test

The test for 3NF is: for each non-key column, does it depend on a non-key column? If the answer is yes for any column, the table is not in 3NF.

Table: employees
Key: id

id             → the key
name           → depends on id (direct)
department_id  → depends on id (direct)
department_name → depends on department_id (transitive) → VIOLATION

The Boyce-Codd Normal Form

Boyce-Codd Normal Form (BCNF) is a stricter version of 3NF. A table is in BCNF when every determinant is a candidate key. The difference appears in tables with multiple candidate keys where a non-key column determines part of a candidate key.

-- 3NF but not BCNF
CREATE TABLE course_instructors (
    student_id   INT,
    course_id    INT,
    instructor_id INT,
    PRIMARY KEY (student_id, course_id),
    UNIQUE (student_id, instructor_id)
);

The instructor_id determines course_id, but instructor_id is not a candidate key. The table is in 3NF because course_id is part of a candidate key, but it is not in BCNF. The BCNF form is rarely needed in practice, and the trade-off is more tables.

The denormalization trade-off

Normalization reduces redundancy but increases the number of joins. A query that needs the employee and the department name must join two tables. On a read-heavy workload, the join cost may outweigh the redundancy cost, and denormalization may be the right choice.

-- Denormalized: one table, no join.
SELECT id, name, department_name FROM employees;

-- Normalized: two tables, one join.
SELECT e.id, e.name, d.name
FROM employees e
JOIN departments d ON d.id = e.department_id;

The decision depends on the workload. A reporting query that reads millions of rows benefits from the denormalized form. A transactional query that updates one row benefits from the normalized form.


Complete Example Session

-- ============================================
-- PART 1: THE UNNORMALIZED TABLE
-- ============================================

CREATE TABLE orders_flat (
    order_id         INT,
    order_date       DATE,
    customer_id      INT,
    customer_name    VARCHAR(100),
    customer_email   VARCHAR(100),
    product_id       INT,
    product_name     VARCHAR(100),
    product_price    DECIMAL(10,2),
    quantity         INT
);

-- Problems:
-- 1. Customer data repeated for every order line.
-- 2. Product data repeated for every order line.
-- 3. No primary key.
-- 4. Update anomalies: changing a customer email
--    requires updating every order line.

-- ============================================
-- PART 2: 1NF — ATOMIC VALUES AND PRIMARY KEY
-- ============================================

-- The table already has atomic values.
-- Add a composite primary key.

CREATE TABLE orders_1nf (
    order_id         INT,
    order_date       DATE,
    customer_id      INT,
    customer_name    VARCHAR(100),
    customer_email   VARCHAR(100),
    product_id       INT,
    product_name     VARCHAR(100),
    product_price    DECIMAL(10,2),
    quantity         INT,
    PRIMARY KEY (order_id, product_id)
);

-- Still violates 2NF: customer_name depends on
-- customer_id, not on the full key.

-- ============================================
-- PART 3: 2NF — REMOVE PARTIAL DEPENDENCIES
-- ============================================

CREATE TABLE order_items (
    order_id    INT,
    product_id  INT,
    quantity    INT,
    PRIMARY KEY (order_id, product_id)
);

CREATE TABLE orders (
    order_id     INT PRIMARY KEY,
    order_date   DATE,
    customer_id  INT
);

CREATE TABLE products (
    product_id     INT PRIMARY KEY,
    product_name   VARCHAR(100),
    product_price  DECIMAL(10,2)
);

CREATE TABLE customers (
    customer_id     INT PRIMARY KEY,
    customer_name   VARCHAR(100),
    customer_email  VARCHAR(100)
);

-- Still violates 3NF if a customer has a department
-- or a product has a category.

-- ============================================
-- PART 4: 3NF — REMOVE TRANSITIVE DEPENDENCIES
-- ============================================

CREATE TABLE categories (
    category_id    INT PRIMARY KEY,
    category_name  VARCHAR(100)
);

ALTER TABLE products
ADD COLUMN category_id INT,
ADD FOREIGN KEY (category_id) REFERENCES categories(category_id);

-- Remove the redundant category_name from products.

-- ============================================
-- PART 5: THE COMPLETE SCHEMA
-- ============================================

CREATE TABLE customers (
    customer_id     INT PRIMARY KEY,
    customer_name   VARCHAR(100) NOT NULL,
    customer_email  VARCHAR(100) UNIQUE NOT NULL
);

CREATE TABLE categories (
    category_id    INT PRIMARY KEY,
    category_name  VARCHAR(100) NOT NULL
);

CREATE TABLE products (
    product_id     INT PRIMARY KEY,
    product_name   VARCHAR(100) NOT NULL,
    product_price  DECIMAL(10,2) NOT NULL,
    category_id    INT,
    FOREIGN KEY (category_id) REFERENCES categories(category_id)
);

CREATE TABLE orders (
    order_id     INT PRIMARY KEY,
    order_date   DATE NOT NULL,
    customer_id  INT NOT NULL,
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);

CREATE TABLE order_items (
    order_id    INT,
    product_id  INT,
    quantity    INT NOT NULL,
    PRIMARY KEY (order_id, product_id),
    FOREIGN KEY (order_id) REFERENCES orders(order_id),
    FOREIGN KEY (product_id) REFERENCES products(product_id)
);

-- ============================================
-- PART 6: THE UPDATE ANOMALY
-- ============================================

-- Change a customer's email.
UPDATE customers SET customer_email = 'new@example.com'
WHERE customer_id = 42;

-- One row updated. No redundancy. No anomaly.

-- ============================================
-- PART 7: THE INSERTION ANOMALY
-- ============================================

-- Add a customer who has not placed an order.
INSERT INTO customers (customer_id, customer_name, customer_email)
VALUES (99, 'New Customer', 'new@example.com');

-- No order needed. The customer exists.

-- ============================================
-- PART 8: THE DELETION ANOMALY
-- ============================================

-- Delete an order without deleting the customer.
DELETE FROM orders WHERE order_id = 1001;

-- The customer remains. The products remain.

-- ============================================
-- PART 9: THE NORMALIZED QUERY
-- ============================================

SELECT
    o.order_id,
    o.order_date,
    c.customer_name,
    p.product_name,
    cat.category_name,
    oi.quantity,
    p.product_price * oi.quantity AS line_total
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
LEFT JOIN categories cat ON cat.category_id = p.category_id;

-- The query joins five tables.
-- The data is consistent.
-- No redundancy.

-- ============================================
-- PART 10: THE DENORMALIZED VIEW
-- ============================================

CREATE VIEW orders_denormalized AS
SELECT
    o.order_id,
    o.order_date,
    c.customer_name,
    p.product_name,
    p.product_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;

-- The view provides the denormalized shape.
-- The storage remains normalized.
-- The best of both: consistency in storage, convenience in queries.

The ten parts showed the unnormalized table, the 1NF fix, the 2NF fix, the 3NF fix, the complete schema, and the three anomalies that the normalization eliminates.


Quick Reference

The Three Normal Forms

FormRuleViolation
1NFAtomic values, unique rowsMulti-valued columns
2NFNo partial dependenciesNon-key column depends on part of the key
3NFNo transitive dependenciesNon-key column depends on another non-key column

Anomalies

AnomalyCauseExample
InsertionCombined entitiesCannot add customer without order
UpdateRepeated dataMust update every row
DeletionCombined entitiesDeleting order deletes customer

Dependency Types

TypeExampleViolates
Partialproduct_name → product_id2NF
Transitivedepartment_name → department_id3NF
Fullquantity → (order_id, product_id)—

Normal Form Tests

TestQuestion
1NFAre all values atomic?
2NFDoes every non-key column depend on the full key?
3NFDoes every non-key column depend only on the key?

Denormalization Trade-off

AspectNormalizedDenormalized
RedundancyNonePresent
JoinsManyFew
Update costLowHigh
Read costHighLow
ConsistencyGuaranteedAt risk

Best Practices

✅ Do This:

-- Store one value per cell.
CREATE TABLE customer_phones (customer_id INT, phone VARCHAR(20));  -- ✅

-- Give every table a primary key.
CREATE TABLE logs (id BIGSERIAL PRIMARY KEY, message TEXT);  -- ✅

-- Move partial dependencies to their own tables.
CREATE TABLE products (id INT PRIMARY KEY, name VARCHAR(100));  -- ✅

-- Move transitive dependencies to their own tables.
CREATE TABLE departments (id INT PRIMARY KEY, name VARCHAR(100));  -- ✅

-- Use foreign keys to enforce the relationships.
FOREIGN KEY (department_id) REFERENCES departments(id);  -- ✅

-- Denormalize only when the workload requires it.
CREATE VIEW orders_denormalized AS SELECT ...;  -- ✅

-- Verify with the dependency tests.
-- For each non-key column, ask what it depends on.  -- ✅

❌ Don’t Do This:

-- Don't store comma-separated lists.
phones VARCHAR(200)  -- ❌ Violates 1NF

-- Don't use repeating columns.
phone1, phone2, phone3  -- ❌ Violates 1NF

-- Don't create a table without a primary key.
CREATE TABLE logs (message TEXT);  -- ❌ Violates 1NF

-- Don't repeat non-key data for convenience.
product_name in order_items  -- ❌ Violates 2NF

-- Don't store derivable data.
department_name in employees  -- ❌ Violates 3NF

-- Don't denormalize prematurely.
-- Start normalized; denormalize when the workload demands it.  -- ❌

-- Don't ignore the anomalies.
-- A redundant column will eventually become inconsistent.  -- ❌

Common Pitfalls

PitfallWhy It HappensFix
Multi-valued columnConvenienceSeparate table
Repeated columnsFixed number of valuesSeparate table
No primary keyTable used as a logAdd a surrogate key
Partial dependencyComposite key with derived dataMove to a separate table
Transitive dependencyNon-key column determines anotherMove to a separate table
Over-normalizationToo many small tablesDenormalize selectively
Missing foreign keysRelationships not enforcedAdd the constraints

Real-World Examples

1. Multi-Valued Column

-- Violation
phones VARCHAR(200)
-- Fix
CREATE TABLE customer_phones (customer_id INT, phone VARCHAR(20));

2. Repeating Columns

-- Violation
phone1, phone2, phone3
-- Fix
CREATE TABLE customer_phones (customer_id INT, phone VARCHAR(20));

3. Partial Dependency

-- Violation
order_items(order_id, product_id, product_name)
-- Fix
products(product_id, product_name)

4. Transitive Dependency

-- Violation
employees(id, department_id, department_name)
-- Fix
departments(department_id, department_name)

5. No Primary Key

-- Violation
CREATE TABLE logs (message TEXT);
-- Fix
CREATE TABLE logs (id BIGSERIAL PRIMARY KEY, message TEXT);

6. Derived Column

-- Violation
order_items(quantity, unit_price, line_total)
-- Fix
-- line_total = quantity * unit_price (computed in the query)

7. The Normalized Join

SELECT c.name, d.name
FROM employees e
JOIN departments d ON d.id = e.department_id;

8. The Denormalized View

CREATE VIEW v AS SELECT ... FROM employees JOIN departments;

9. The 3NF Test

-- For each non-key column, identify what determines it.
-- If it is not the key, it is a transitive dependency.

10. The Boyce-Codd Case

-- A table with multiple candidate keys where a non-key
-- column determines part of a candidate key is in 3NF
-- but not BCNF.

Visual

The Normalization Flow

┌────────────────────────────────────────────────────────────┐
│  NORMALIZATION PIPELINE                                    │
│                                                            │
│  UNNORMALIZED                                              │
│  ┌──────────────────────────────────────────────────┐     │
│  │  order_items_flat:                                │     │
│  │    order_id, product_id, quantity,                │     │
│  │    product_name, product_price,                   │     │
│  │    customer_name, customer_email                  │     │
│  └──────────────────────────────────────────────────┘     │
│                        │                                   │
│                        │  1NF: atomic values, key         │
│                        v                                   │
│  ┌──────────────────────────────────────────────────┐     │
│  │  order_items_1nf:                                 │     │
│  │    PRIMARY KEY (order_id, product_id)             │     │
│  │    Still has partial dependencies.                │     │
│  └──────────────────────────────────────────────────┘     │
│                        │                                   │
│                        │  2NF: remove partial            │
│                        v                                   │
│  ┌──────────────────────────────────────────────────┐     │
│  │  order_items (order_id, product_id, quantity)     │     │
│  │  products (product_id, product_name, price)       │     │
│  │  customers (customer_id, name, email)             │     │
│  │  Still has transitive dependencies.               │     │
│  └──────────────────────────────────────────────────┘     │
│                        │                                   │
│                        │  3NF: remove transitive         │
│                        v                                   │
│  ┌──────────────────────────────────────────────────┐     │
│  │  order_items (order_id, product_id, quantity)     │     │
│  │  orders (order_id, order_date, customer_id)       │     │
│  │  products (product_id, name, price, category_id)  │     │
│  │  customers (customer_id, name, email)             │     │
│  │  categories (category_id, name)                   │     │
│  └──────────────────────────────────────────────────┘     │
│                                                            │
│  Each step eliminates a specific class of anomaly.         │
└────────────────────────────────────────────────────────────┘

The Three Anomalies

┌────────────────────────────────────────────────────────────┐
│  ANOMALIES CAUSED BY REDUNDANCY                            │
│                                                            │
│  INSERTION ANOMALY                                         │
│  ┌──────────────────────────────────────────────────┐     │
│  │  Cannot insert a customer without an order.       │     │
│  │  The primary key requires the order_id.           │     │
│  └──────────────────────────────────────────────────┘     │
│                                                            │
│  UPDATE ANOMALY                                            │
│  ┌──────────────────────────────────────────────────┐     │
│  │  Changing a customer's email requires updating    │     │
│  │  every row where the customer appears.            │     │
│  │  A missed row leaves contradictory data.          │     │
│  └──────────────────────────────────────────────────┘     │
│                                                            │
│  DELETION ANOMALY                                          │
│  ┌──────────────────────────────────────────────────┐     │
│  │  Deleting an order deletes the customer's data    │     │
│  │  as a side effect.                                │     │
│  └──────────────────────────────────────────────────┘     │
│                                                            │
│  Normalization eliminates all three by storing each       │
│  fact in exactly one place.                                │
└────────────────────────────────────────────────────────────┘

The Dependency Types

┌────────────────────────────────────────────────────────────┐
│  DEPENDENCY TYPES                                          │
│                                                            │
│  FULL DEPENDENCY                                           │
│  ┌──────────────────────────────────────────────────┐     │
│  │  quantity → (order_id, product_id)                │     │
│  │  The quantity depends on both parts of the key.   │     │
│  │  Correct.                                         │     │
│  └──────────────────────────────────────────────────┘     │
│                                                            │
│  PARTIAL DEPENDENCY                                        │
│  ┌──────────────────────────────────────────────────┐     │
│  │  product_name → product_id                        │     │
│  │  Depends on part of the key.                      │     │
│  │  Violates 2NF.                                    │     │
│  └──────────────────────────────────────────────────┘     │
│                                                            │
│  TRANSITIVE DEPENDENCY                                     │
│  ┌──────────────────────────────────────────────────┐     │
│  │  department_name → department_id                  │     │
│  │  A non-key column determines another.             │     │
│  │  Violates 3NF.                                    │     │
│  └──────────────────────────────────────────────────┘     │
└────────────────────────────────────────────────────────────┘

The Normalized Schema

┌────────────────────────────────────────────────────────────┐
│  NORMALIZED SCHEMA                                         │
│                                                            │
│  customers                                                 │
│    customer_id (PK), name, email                          │
│                                                            │
│  orders                                                    │
│    order_id (PK), order_date, customer_id (FK)            │
│                                                            │
│  order_items                                               │
│    order_id (PK, FK), product_id (PK, FK), quantity       │
│                                                            │
│  products                                                  │
│    product_id (PK), name, price, category_id (FK)         │
│                                                            │
│  categories                                                │
│    category_id (PK), name                                  │
│                                                            │
│  ┌──────────────────────────────────────────────────┐     │
│  │  Each fact is stored in exactly one place.        │     │
│  │  Updates touch one row.                           │     │
│  │  Deletes do not destroy unrelated data.           │     │
│  │  Inserts do not require unrelated entities.       │     │
│  └──────────────────────────────────────────────────┘     │
└────────────────────────────────────────────────────────────┘

Summary

ItemValue
1NFAtomic values, unique rows, no repeating groups
2NF1NF plus no partial dependencies
3NF2NF plus no transitive dependencies
BCNFStricter than 3NF; every determinant is a candidate key
Partial dependencyNon-key column depends on part of a composite key
Transitive dependencyNon-key column depends on another non-key column
Insertion anomalyCannot insert one entity without another
Update anomalyRedundant data requires multiple updates
Deletion anomalyDeleting one entity destroys another
DenormalizationReintroducing redundancy for read performance

Key takeaways:

  • Normalization eliminates redundancy by storing each fact in one place. A fact that is stored once cannot become inconsistent. The three normal forms each address a specific kind of redundancy.
  • 1NF requires atomic values and a primary key. A column that holds a list of values, or a set of repeating columns, violates 1NF. The fix is a separate table with one row per value.
  • 2NF eliminates partial dependencies. In a table with a composite key, a non-key column that depends on only part of the key is a partial dependency. The fix is to move the column to the table that corresponds to the part of the key it depends on.
  • 3NF eliminates transitive dependencies. A non-key column that depends on another non-key column is a transitive dependency. The fix is to move the dependent column to its own table, keyed by the column that determines it.
  • Each normal form fixes a specific anomaly. 1NF fixes the multi-value problem. 2NF fixes the partial-dependency problem. 3NF fixes the transitive-dependency problem. The anomalies are insertion, update, and deletion, and each is a symptom of redundancy.
  • Denormalization is a deliberate trade-off. Normalized schemas require more joins, which cost read performance. When the read workload dominates and the redundancy is acceptable, denormalization is the right choice. The decision is driven by the workload, not by theory.
  • The three normal forms are sufficient for most schemas. 3NF eliminates the anomalies that matter in practice. BCNF and the higher forms address edge cases that appear rarely. Most production schemas stop at 3NF.
  • The tests are simple. For 1NF, ask whether every value is atomic. For 2NF, ask whether every non-key column depends on the full key. For 3NF, ask whether every non-key column depends only on the key. The answers determine the form.

Remember: Normalization is the discipline of storing each fact in exactly one place. The first three normal forms describe the rules: atomic values, full dependencies, and no transitive dependencies. Each form fixes a specific anomaly: the multi-value problem, the partial-dependency problem, and the transitive-dependency problem. The anomalies are not theoretical; they cause real bugs when a redundant value is updated in one place and not another. The fix is to move the redundant column to its own table and reference it by key. The cost is more joins, which matters for read-heavy workloads. Denormalization is the response when the join cost dominates, and it is a deliberate trade-off, not a return to the unnormalized form. The result is a schema where updates are local, deletes are safe, inserts are unconstrained, and the data is consistent by construction.



Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!