SQLDatabase Designbeginner
Updated:

Database Normalization

7 min read

A practical walk through 1NF, 2NF, 3NF and BCNF using an employees and orders schema, showing how normalization removes redundancy and update anomalies.

TL;DR – Quick Answer

Database normalization is the process of organizing tables to reduce redundant data and prevent update anomalies. You split one wide table into related tables and connect them with keys, following rules called normal forms. The common target is Third Normal Form (3NF), where every non-key column depends only on the primary key.

On This Page

Bad table design does not announce itself. Everything works in a demo, then months later a customer changes their address and it updates in one place but not three others, and now your reports disagree with each other. Normalization is the discipline that prevents this by making sure every fact lives in exactly one place.

We will start with one deliberately messy table and refine it step by step into a clean design, using the employees and orders example from the SQL learning hub. By the end you will be able to spot a poorly designed schema in seconds.

The problem: one big table

Suppose a beginner stores everything about orders in a single table:

CREATE TABLE orders_bad (
    order_id      INT,
    order_date    DATE,
    emp_id        INT,
    emp_name      VARCHAR(100),
    emp_dept      VARCHAR(50),
    dept_location VARCHAR(50),
    products      VARCHAR(255)   -- e.g. 'Keyboard, Mouse'
);

This table has three real problems. The same employee's name and department repeat on every order they handle (redundancy). If an employee moves department, you must update every one of their order rows, and if you miss one the data becomes inconsistent (update anomaly). You cannot record a new employee who has no orders yet without a fake order (insertion anomaly), and deleting an employee's only order erases the fact they exist (deletion anomaly). Normalization removes all four.

Pro tip: Every anomaly comes from storing the same fact in more than one place. If you find yourself copy-pasting a value across rows, that is the signal a second table is trying to be born.

First Normal Form (1NF): atomic values

A table is in 1NF when every column holds a single, indivisible value and there are no repeating groups. The products column above violates this — 'Keyboard, Mouse' packs multiple values into one cell, which makes questions like "how many keyboards did we sell?" nearly impossible.

To reach 1NF, give each product its own row by splitting order lines out:

CREATE TABLE order_items (
    order_id    INT,
    product     VARCHAR(100),
    quantity    INT,
    PRIMARY KEY (order_id, product)
);

Now each row holds one product for one order. No cell contains a list. This alone unlocks proper filtering, counting, and the aggregation with GROUP BY you will use constantly.

Second Normal Form (2NF): no partial dependencies

2NF applies when a table has a composite primary key — a key made of more than one column. A table is in 2NF if it is in 1NF and every non-key column depends on the whole key, not just part of it.

In order_items, the key is (order_id, product). Now imagine we had added a product_price column. Price depends only on product, not on the combination of order and product — that is a partial dependency. The fix is to move product attributes into their own table:

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

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

Now a product's price is stored once in products. Change it there and every order line reflects it through the JOIN, with no risk of stale copies.

Third Normal Form (3NF): no transitive dependencies

3NF is the target for most business databases. A table is in 3NF if it is in 2NF and no non-key column depends on another non-key column — a transitive dependency.

Look again at the original table: emp_id determines emp_name and emp_dept, and emp_dept determines dept_location. So dept_location depends on emp_dept, which is not the key. That is transitive. The clean design separates each entity into its own table:

CREATE TABLE departments (
    dept_id       INT PRIMARY KEY,
    dept_name     VARCHAR(50),
    dept_location VARCHAR(50)
);

CREATE TABLE employees (
    emp_id    INT PRIMARY KEY,
    name      VARCHAR(100),
    dept_id   INT,
    salary    DECIMAL(10, 2),
    FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
);

CREATE TABLE orders (
    order_id   INT PRIMARY KEY,
    emp_id     INT,
    order_date DATE,
    FOREIGN KEY (emp_id) REFERENCES employees(emp_id)
);

Now each fact lives once. A department's location is in departments. An employee's department is a single dept_id. Move an employee to a new department by updating one column in one row, and every order they handled automatically reflects the change through the foreign keys.

Interview note: "Explain 1NF, 2NF, and 3NF with an example" is one of the most common database interview questions for freshers. Memorize this one-line version: 1NF removes repeating groups, 2NF removes partial dependencies on part of a composite key, 3NF removes transitive dependencies between non-key columns.

BCNF and beyond

Boyce-Codd Normal Form (BCNF) is a stricter version of 3NF. It handles rare edge cases where a table has multiple overlapping candidate keys and a non-trivial dependency exists on a column that is not a candidate key. For the vast majority of schemas, a table in 3NF is already in BCNF, so you will not touch BCNF often. Higher forms — 4NF and 5NF — deal with multi-valued and join dependencies and appear mostly in theory courses, not day-to-day design.

When to denormalize

Normalization optimizes for correctness and storage. It sometimes costs read speed, because reassembling data requires joins. In analytics and reporting workloads, where you read enormous volumes and rarely update, teams deliberately denormalize — copying the department name directly onto the order table so a dashboard query needs no join.

Denormalization is a trade you make on purpose, after measuring, accepting controlled redundancy for speed. This is also part of why some systems choose non-relational databases entirely; the SQL vs NoSQL comparison and SQL vs NoSQL trade-offs explore that decision.

Common mistake: Denormalizing early "for performance" before you have a real query that is measurably slow. Start normalized, measure, and only add redundancy where the numbers justify it. Premature denormalization creates exactly the anomalies you worked to remove.

Functional dependencies: the idea underneath every normal form

Every normal form is really a statement about functional dependencies — which columns determine which other columns. Writing "A → B" means that if you know A, you know exactly one B. In the messy table, emp_id → emp_name (an id determines one name) and emp_dept → dept_location (a department determines one location).

Normalization is just the practice of making sure every non-key column depends on the key, the whole key, and nothing but the key — a phrase worth memorizing because it summarizes 2NF and 3NF in one line. When a column depends on something other than the full primary key, you have found a table that wants to be split. Train yourself to write these arrows out for a messy table and the correct decomposition becomes almost mechanical.

Consider a quick test on the original design. Does dept_location depend on order_id? No — it depends on the department, which depends on the employee. That two-hop chain is the transitive dependency 3NF forbids, and spotting it is the whole skill. Once every table passes "depends on the key, the whole key, and nothing but the key," you are in 3NF and your update anomalies are gone.

A design workflow you can reuse

When you design a new schema, work in this order. List every distinct real-world entity — employee, department, product, order. Give each its own table with a primary key. For every relationship between entities, decide whether it is one-to-many (use a foreign key on the "many" side) or many-to-many (use a link table like order_items). Then walk each table and ask whether any non-key column depends on something other than the key; if so, split it out. Finally, add the foreign keys that connect everything.

This workflow lands you in 3NF naturally, without ever memorizing definitions. It is exactly how experienced developers design the data layer of a real application, entity by entity, before writing a line of backend code.

The keys that hold it together

Normalization depends on keys. A primary key uniquely identifies each row. A foreign key in one table references the primary key of another, enforcing that you cannot record an order for an employee who does not exist. These constraints are also what the ACID guarantees in transactions enforce as "consistency" — a transaction that would orphan a foreign key is rejected automatically.

Bringing it together

Normalization is a step-by-step refinement: make values atomic (1NF), remove partial dependencies on composite keys (2NF), remove transitive dependencies between non-key columns (3NF), and connect the resulting tables with primary and foreign keys. The payoff is a schema where every fact lives once, updates touch one row, and your data stays internally consistent as the application grows. When you design backends in the Java Full Stack with AI program, this is the foundation every entity and repository sits on — get the schema right and everything above it becomes simpler.

Frequently Asked Questions

What are the main normal forms?
The most used are First Normal Form (1NF), Second Normal Form (2NF), Third Normal Form (3NF), and Boyce-Codd Normal Form (BCNF). Each builds on the previous one by removing a specific kind of redundancy or dependency. Most business databases aim for 3NF.
Why is normalization important?
It removes duplicate data so a fact is stored in exactly one place. That prevents update anomalies, where changing a value in one row leaves stale copies in others, and it keeps the database smaller and more consistent over time.
What is the difference between 2NF and 3NF?
2NF removes partial dependencies, where a column depends on only part of a composite key. 3NF removes transitive dependencies, where a non-key column depends on another non-key column instead of directly on the primary key.
Is more normalization always better?
No. Higher normalization removes redundancy but can require more joins, which slows some read-heavy queries. Analytics and reporting systems often deliberately denormalize for speed, while transactional systems favor 3NF for correctness.
What is denormalization?
Denormalization is intentionally adding redundant data back into a schema to avoid expensive joins and speed up reads. It is a deliberate trade you make after measuring, accepting some duplication in exchange for faster queries.

Want to Build Your Career in Java Full Stack with AI?

Join CodeBegun and train with working industry engineers — Explore the Program

Apply for Demo Class →
Siva Prasad Galaba
Founder, CodeBegun · Staff Engineer

Founder of CodeBegun. 15+ years building Java systems at companies like Crunchyroll. Teaches Java, Spring Boot and system design the way the industry actually works, and mentors students through projects, mock interviews and placement preparation.

Technically reviewed by CodeBegun Technical TeamLast reviewed 15 July 2026 LinkedIn
Chat with us