Lý thuyết
45 phút
Bài 3/12

Normalization - Chuẩn hóa Cơ sở dữ liệu

Tìm hiểu các dạng chuẩn 1NF, 2NF, 3NF, BCNF và cách áp dụng để thiết kế database hiệu quả

Normalization - Chuẩn hóa Cơ sở dữ liệu

1. Normalization là gì?

Normalization là quá trình tổ chức dữ liệu trong database để:

  • Loại bỏ Data Redundancy (dữ liệu trùng lặp)
  • Đảm bảo Data Integrity (toàn vẹn dữ liệu)
  • Tránh Anomalies (các vấn đề bất thường)

Các vấn đề khi không Normalize

Database Anomalies

Anomalies
Insert Anomaly
Cannot add new data without related data
Update Anomaly
Must update many rows for 1 change
Delete Anomaly
Deleting data removes other data

Ví dụ: Table chưa Normalize

SQL
1-- ❌ BAD: Table không normalize
2CREATE TABLE OrderInfo (
3 OrderID INT,
4 OrderDate DATE,
5 CustomerID INT,
6 CustomerName NVARCHAR(100), -- Redundant!
7 CustomerEmail NVARCHAR(100), -- Redundant!
8 ProductID INT,
9 ProductName NVARCHAR(100), -- Redundant!
10 UnitPrice DECIMAL(10,2), -- Redundant!
11 Quantity INT,
12 TotalAmount DECIMAL(10,2)
13);
14
15-- Insert data
16INSERT INTO OrderInfo VALUES
17(1, '2024-01-15', 101, 'Nguyen Van A', 'a@email.com', 1001, 'Laptop', 1500.00, 2, 3000.00),
18(1, '2024-01-15', 101, 'Nguyen Van A', 'a@email.com', 1002, 'Mouse', 25.00, 3, 75.00),
19(2, '2024-01-16', 101, 'Nguyen Van A', 'a@email.com', 1001, 'Laptop', 1500.00, 1, 1500.00);

Problems:

  • CustomerName lặp lại 3 lần → Redundancy
  • Đổi tên Customer phải update 3 rows → Update Anomaly
  • Xóa Order 2 sẽ không còn thông tin Product 1001 → Delete Anomaly

2. Các dạng chuẩn (Normal Forms)

Normalization Journey

1
Unnormalized
2
1NF
3
2NF
4
3NF
5
BCNF
Normal FormRequirement
1NFAtomic values, no repeating groups
2NF1NF + No partial dependencies
3NF2NF + No transitive dependencies
BCNF3NF + Every determinant is a candidate key
4NFBCNF + No multi-valued dependencies
5NF4NF + No join dependencies

💡 Thực tế: Hầu hết databases chỉ cần đạt 3NF là đủ!


3. First Normal Form (1NF)

Quy tắc 1NF

Text
11. Mỗi column chứa ATOMIC values (giá trị không thể chia nhỏ)
22. Mỗi column có UNIQUE name
33. Mỗi row là UNIQUE (có Primary Key)
44. KHÔNG có repeating groups

Ví dụ: Vi phạm 1NF

SQL
1-- ❌ Vi phạm 1NF: Repeating groups & Non-atomic
2CREATE TABLE StudentCourses (
3 StudentID INT,
4 StudentName NVARCHAR(100),
5 Courses NVARCHAR(500) -- "Math, Physics, Chemistry" ← Non-atomic!
6);
7
8-- Hoặc
9CREATE TABLE StudentCourses2 (
10 StudentID INT,
11 StudentName NVARCHAR(100),
12 Course1 NVARCHAR(50), -- Repeating groups!
13 Course2 NVARCHAR(50),
14 Course3 NVARCHAR(50)
15);

Chuyển sang 1NF

SQL
1-- ✅ Đạt 1NF: Atomic values, no repeating groups
2CREATE TABLE Student (
3 StudentID INT PRIMARY KEY,
4 StudentName NVARCHAR(100)
5);
6
7CREATE TABLE Enrollment (
8 StudentID INT,
9 CourseCode VARCHAR(10),
10 PRIMARY KEY (StudentID, CourseCode),
11 FOREIGN KEY (StudentID) REFERENCES Student(StudentID)
12);
13
14-- Data
15INSERT INTO Student VALUES (1, 'Nguyen Van A');
16INSERT INTO Enrollment VALUES (1, 'MATH101');
17INSERT INTO Enrollment VALUES (1, 'PHYS101');
18INSERT INTO Enrollment VALUES (1, 'CHEM101');

AdventureWorks 1NF Example

SQL
1-- Person.Person - Đã đạt 1NF
2-- Name được tách thành các atomic columns
3SELECT
4 BusinessEntityID,
5 Title, -- Atomic
6 FirstName, -- Atomic (không phải "Full Name")
7 MiddleName, -- Atomic
8 LastName, -- Atomic
9 Suffix -- Atomic
10FROM Person.Person
11WHERE BusinessEntityID <= 5;

4. Second Normal Form (2NF)

Quy tắc 2NF

Text
11. Đạt 1NF
22. KHÔNG có Partial Dependency
3 - Mọi non-key column phải phụ thuộc vào TOÀN BỘ primary key
4 - Chỉ áp dụng khi PK là composite (nhiều columns)

Ví dụ: Vi phạm 2NF

SQL
1-- ❌ Vi phạm 2NF: Partial Dependency
2CREATE TABLE OrderDetail (
3 OrderID INT,
4 ProductID INT,
5 ProductName NVARCHAR(100), -- Chỉ phụ thuộc ProductID, không phụ thuộc OrderID
6 ProductPrice DECIMAL(10,2), -- Chỉ phụ thuộc ProductID
7 Quantity INT,
8 PRIMARY KEY (OrderID, ProductID)
9);
10
11-- ProductName phụ thuộc ProductID (một phần của PK) → Partial Dependency

Functional Dependencies:

Text
1OrderID, ProductID → Quantity ✅ Full dependency
2ProductID → ProductName ❌ Partial dependency
3ProductID → ProductPrice ❌ Partial dependency

Chuyển sang 2NF

SQL
1-- ✅ Đạt 2NF: Tách Product ra table riêng
2CREATE TABLE Product (
3 ProductID INT PRIMARY KEY,
4 ProductName NVARCHAR(100),
5 ProductPrice DECIMAL(10,2)
6);
7
8CREATE TABLE OrderDetail (
9 OrderID INT,
10 ProductID INT,
11 Quantity INT,
12 UnitPrice DECIMAL(10,2), -- Giá tại thời điểm đặt hàng (có thể khác ProductPrice)
13 PRIMARY KEY (OrderID, ProductID),
14 FOREIGN KEY (ProductID) REFERENCES Product(ProductID)
15);

AdventureWorks 2NF Example

SQL
1-- Sales.SalesOrderDetail đạt 2NF
2-- Thông tin Product được tách riêng
3SELECT
4 sod.SalesOrderID,
5 sod.SalesOrderDetailID,
6 sod.ProductID,
7 sod.OrderQty,
8 sod.UnitPrice, -- Stored tại thời điểm order
9 sod.LineTotal,
10 p.Name AS ProductName -- Lấy từ table Product
11FROM Sales.SalesOrderDetail sod
12JOIN Production.Product p ON sod.ProductID = p.ProductID
13WHERE sod.SalesOrderID = 43659;

5. Third Normal Form (3NF)

Quy tắc 3NF

Text
11. Đạt 2NF
22. KHÔNG có Transitive Dependency
3 - Non-key column KHÔNG được phụ thuộc vào non-key column khác
4 - Mọi non-key phải phụ thuộc TRỰC TIẾP vào Primary Key

Ví dụ: Vi phạm 3NF

SQL
1-- ❌ Vi phạm 3NF: Transitive Dependency
2CREATE TABLE Employee (
3 EmployeeID INT PRIMARY KEY,
4 EmployeeName NVARCHAR(100),
5 DepartmentID INT,
6 DepartmentName NVARCHAR(100), -- Phụ thuộc DepartmentID
7 DepartmentManager NVARCHAR(100) -- Phụ thuộc DepartmentID
8);
9
10-- Transitive: EmployeeID → DepartmentID → DepartmentName

Functional Dependencies:

Text
1EmployeeID → EmployeeName ✅ Direct
2EmployeeID → DepartmentID ✅ Direct
3DepartmentID → DepartmentName ❌ Transitive (non-key → non-key)
4DepartmentID → DepartmentManager ❌ Transitive

Chuyển sang 3NF

SQL
1-- ✅ Đạt 3NF: Tách Department ra table riêng
2CREATE TABLE Department (
3 DepartmentID INT PRIMARY KEY,
4 DepartmentName NVARCHAR(100),
5 DepartmentManager NVARCHAR(100)
6);
7
8CREATE TABLE Employee (
9 EmployeeID INT PRIMARY KEY,
10 EmployeeName NVARCHAR(100),
11 DepartmentID INT,
12 FOREIGN KEY (DepartmentID) REFERENCES Department(DepartmentID)
13);

AdventureWorks 3NF Example

SQL
1-- HumanResources module đạt 3NF
2-- Employee và Department được tách riêng
3
4-- Employee table
5SELECT
6 e.BusinessEntityID,
7 p.FirstName,
8 p.LastName,
9 e.JobTitle,
10 e.HireDate,
11 d.Name AS DepartmentName, -- Từ Department table
12 d.GroupName -- Từ Department table
13FROM HumanResources.Employee e
14JOIN Person.Person p ON e.BusinessEntityID = p.BusinessEntityID
15JOIN HumanResources.EmployeeDepartmentHistory edh
16 ON e.BusinessEntityID = edh.BusinessEntityID
17 AND edh.EndDate IS NULL
18JOIN HumanResources.Department d ON edh.DepartmentID = d.DepartmentID
19WHERE e.BusinessEntityID <= 10;

6. Boyce-Codd Normal Form (BCNF)

Quy tắc BCNF

Text
11. Đạt 3NF
22. Mọi DETERMINANT phải là Candidate Key
3 - Determinant: column(s) mà columns khác phụ thuộc vào

Ví dụ: Vi phạm BCNF

SQL
1-- Scenario: 1 Student chỉ có 1 Advisor per Subject
2-- 1 Advisor chỉ dạy 1 Subject
3CREATE TABLE StudentAdvisor (
4 StudentID INT,
5 Subject VARCHAR(50),
6 AdvisorID INT,
7 PRIMARY KEY (StudentID, Subject)
8);
9
10-- Functional Dependencies:
11-- StudentID, Subject → AdvisorID ✅ (PK determines non-key)
12-- AdvisorID → Subject ❌ (non-key determines non-key)

Chuyển sang BCNF

SQL
1-- ✅ Đạt BCNF
2CREATE TABLE Advisor (
3 AdvisorID INT PRIMARY KEY,
4 Subject VARCHAR(50)
5);
6
7CREATE TABLE StudentAdvisor (
8 StudentID INT,
9 AdvisorID INT,
10 PRIMARY KEY (StudentID, AdvisorID),
11 FOREIGN KEY (AdvisorID) REFERENCES Advisor(AdvisorID)
12);

7. Denormalization - Khi nào KHÔNG normalize?

Tại sao Denormalize?

Text
1Performance > Purity
NormalizeDenormalize
Ít redundancyCó redundancy
Nhiều JOINsÍt JOINs
Insert/Update nhanhSELECT nhanh
OLTP systemsOLAP/Reporting systems

Ví dụ Denormalization

SQL
1-- ✅ Normalized (OLTP)
2SELECT
3 c.CustomerName,
4 o.OrderDate,
5 p.ProductName,
6 od.Quantity,
7 od.UnitPrice
8FROM Orders o
9JOIN Customers c ON o.CustomerID = c.CustomerID
10JOIN OrderDetails od ON o.OrderID = od.OrderID
11JOIN Products p ON od.ProductID = p.ProductID;
12-- 4 tables, 3 JOINs
13
14-- 📊 Denormalized for Reporting (OLAP)
15CREATE TABLE SalesReport (
16 OrderID INT,
17 OrderDate DATE,
18 CustomerID INT,
19 CustomerName NVARCHAR(100), -- Denormalized
20 ProductID INT,
21 ProductName NVARCHAR(100), -- Denormalized
22 Quantity INT,
23 UnitPrice DECIMAL(10,2),
24 TotalAmount DECIMAL(10,2) -- Pre-calculated
25);
26-- 1 table, 0 JOINs

AdventureWorks Denormalization

SQL
1-- AdventureWorks có cả normalized và denormalized structures
2
3-- Normalized: Production.Product + Production.ProductSubcategory + Production.ProductCategory
4SELECT
5 p.ProductID,
6 p.Name,
7 ps.Name AS Subcategory,
8 pc.Name AS Category
9FROM Production.Product p
10JOIN Production.ProductSubcategory ps ON p.ProductSubcategoryID = ps.ProductSubcategoryID
11JOIN Production.ProductCategory pc ON ps.ProductCategoryID = pc.ProductCategoryID;
12
13-- Pre-calculated in SalesOrderDetail
14SELECT
15 SalesOrderDetailID,
16 OrderQty,
17 UnitPrice,
18 UnitPriceDiscount,
19 LineTotal -- Pre-calculated: OrderQty * UnitPrice * (1 - UnitPriceDiscount)
20FROM Sales.SalesOrderDetail
21WHERE SalesOrderID = 43659;

8. Hands-on: Normalize a Table

Exercise: Normalize này

SQL
1-- Unnormalized table
2CREATE TABLE BookingInfo (
3 BookingID INT,
4 GuestName NVARCHAR(100),
5 GuestEmail NVARCHAR(100),
6 GuestPhone NVARCHAR(20),
7 RoomNumber INT,
8 RoomType NVARCHAR(50), -- "Single", "Double", "Suite"
9 RoomPrice DECIMAL(10,2),
10 CheckInDate DATE,
11 CheckOutDate DATE,
12 Services NVARCHAR(500), -- "Breakfast, Spa, Airport Pickup"
13 TotalAmount DECIMAL(10,2)
14);

Solution Steps

Step 1: Identify Issues

  • Services không atomic (vi phạm 1NF)
  • RoomType, RoomPrice phụ thuộc RoomNumber (vi phạm 2NF nếu composite PK)
  • GuestEmail, GuestPhone nên tách riêng

Step 2: Apply 1NF

SQL
1-- Tách Services ra
2CREATE TABLE BookingService (
3 BookingID INT,
4 ServiceID INT,
5 PRIMARY KEY (BookingID, ServiceID)
6);

Step 3: Apply 2NF & 3NF

SQL
1-- Guest table
2CREATE TABLE Guest (
3 GuestID INT PRIMARY KEY IDENTITY(1,1),
4 GuestName NVARCHAR(100),
5 GuestEmail NVARCHAR(100),
6 GuestPhone NVARCHAR(20)
7);
8
9-- Room table
10CREATE TABLE Room (
11 RoomNumber INT PRIMARY KEY,
12 RoomTypeID INT,
13 FOREIGN KEY (RoomTypeID) REFERENCES RoomType(RoomTypeID)
14);
15
16-- RoomType table
17CREATE TABLE RoomType (
18 RoomTypeID INT PRIMARY KEY IDENTITY(1,1),
19 RoomTypeName NVARCHAR(50),
20 BasePrice DECIMAL(10,2)
21);
22
23-- Service table
24CREATE TABLE Service (
25 ServiceID INT PRIMARY KEY IDENTITY(1,1),
26 ServiceName NVARCHAR(50),
27 ServicePrice DECIMAL(10,2)
28);
29
30-- Booking table
31CREATE TABLE Booking (
32 BookingID INT PRIMARY KEY IDENTITY(1,1),
33 GuestID INT,
34 RoomNumber INT,
35 CheckInDate DATE,
36 CheckOutDate DATE,
37 TotalAmount DECIMAL(10,2),
38 FOREIGN KEY (GuestID) REFERENCES Guest(GuestID),
39 FOREIGN KEY (RoomNumber) REFERENCES Room(RoomNumber)
40);
41
42-- BookingService (junction table)
43CREATE TABLE BookingService (
44 BookingID INT,
45 ServiceID INT,
46 PRIMARY KEY (BookingID, ServiceID),
47 FOREIGN KEY (BookingID) REFERENCES Booking(BookingID),
48 FOREIGN KEY (ServiceID) REFERENCES Service(ServiceID)
49);

9. Summary Checklist

markdown
1## Normalization Checklist
2
3### 1NF ✅
4- [ ] All columns have atomic values
5- [ ] No repeating groups
6- [ ] Each row is unique (has PK)
7
8### 2NF ✅
9- [ ] Is in 1NF
10- [ ] No partial dependencies (all non-key columns depend on entire PK)
11
12### 3NF ✅
13- [ ] Is in 2NF
14- [ ] No transitive dependencies (non-key doesn't depend on non-key)
15
16### BCNF ✅
17- [ ] Is in 3NF
18- [ ] Every determinant is a candidate key

Tiếp theo

Bài tiếp theo: Keys & Constraints - học về Primary Key, Foreign Key, Unique, Check constraints và cách implement trong SQL Server!