🎯 Mục tiêu bài học
Sau bài học này, bạn sẽ:
- ✅ Hiểu Normalization là gì và tại sao cần normalize
- ✅ Nắm vững các dạng chuẩn: 1NF, 2NF, 3NF, BCNF
- ✅ Biết cách phát hiện và xử lý các loại dependency
- ✅ Hiểu khi nào nên Denormalize
- ✅ Thực hành normalize một bảng dữ liệu thực tế
🔍 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
Ví dụ: Table chưa Normalize
1-- ❌ BAD: Table không normalize2CREATE 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);1415-- Insert data16INSERT 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
Checkpoint
Ba loại anomaly khi không normalize database là gì? Cho ví dụ cụ thể cho từng loại.
📋 Các dạng chuẩn (Normal Forms)
Normalization Journey
| Normal Form | Requirement |
|---|---|
| 1NF | Atomic values, no repeating groups |
| 2NF | 1NF + No partial dependencies |
| 3NF | 2NF + No transitive dependencies |
| BCNF | 3NF + Every determinant is a candidate key |
| 4NF | BCNF + No multi-valued dependencies |
| 5NF | 4NF + No join dependencies |
💡 Thực tế: Hầu hết databases chỉ cần đạt 3NF là đủ!
Checkpoint
Trong thực tế, database thường được normalize đến dạng chuẩn nào? Tại sao?
🔑 First Normal Form (1NF)
Quy tắc 1NF
11. Mỗi column chứa ATOMIC values (giá trị không thể chia nhỏ)22. Mỗi column có UNIQUE name33. Mỗi row là UNIQUE (có Primary Key)44. KHÔNG có repeating groupsVí dụ: Vi phạm 1NF
1-- ❌ Vi phạm 1NF: Repeating groups & Non-atomic2CREATE TABLE StudentCourses (3 StudentID INT,4 StudentName NVARCHAR(100),5 Courses NVARCHAR(500) -- "Math, Physics, Chemistry" ← Non-atomic!6);78-- Hoặc9CREATE 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
1-- ✅ Đạt 1NF: Atomic values, no repeating groups2CREATE TABLE Student (3 StudentID INT PRIMARY KEY,4 StudentName NVARCHAR(100)5);67CREATE TABLE Enrollment (8 StudentID INT,9 CourseCode VARCHAR(10),10 PRIMARY KEY (StudentID, CourseCode),11 FOREIGN KEY (StudentID) REFERENCES Student(StudentID)12);1314-- Data15INSERT 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
1-- Person.Person - Đã đạt 1NF2-- Name được tách thành các atomic columns3SELECT 4 BusinessEntityID,5 Title, -- Atomic6 FirstName, -- Atomic (không phải "Full Name")7 MiddleName, -- Atomic8 LastName, -- Atomic9 Suffix -- Atomic10FROM Person.Person11WHERE BusinessEntityID <= 5;Checkpoint
1NF yêu cầu gì về giá trị trong mỗi column? Cho ví dụ vi phạm 1NF và cách sửa.
📊 Second Normal Form (2NF)
Quy tắc 2NF
11. Đạt 1NF22. KHÔNG có Partial Dependency3 - Mọi non-key column phải phụ thuộc vào TOÀN BỘ primary key4 - Chỉ áp dụng khi PK là composite (nhiều columns)Ví dụ: Vi phạm 2NF
1-- ❌ Vi phạm 2NF: Partial Dependency2CREATE TABLE OrderDetail (3 OrderID INT,4 ProductID INT,5 ProductName NVARCHAR(100), -- Chỉ phụ thuộc ProductID, không phụ thuộc OrderID6 ProductPrice DECIMAL(10,2), -- Chỉ phụ thuộc ProductID7 Quantity INT,8 PRIMARY KEY (OrderID, ProductID)9);1011-- ProductName phụ thuộc ProductID (một phần của PK) → Partial DependencyFunctional Dependencies:
1OrderID, ProductID → Quantity ✅ Full dependency2ProductID → ProductName ❌ Partial dependency3ProductID → ProductPrice ❌ Partial dependencyChuyển sang 2NF
1-- ✅ Đạt 2NF: Tách Product ra table riêng2CREATE TABLE Product (3 ProductID INT PRIMARY KEY,4 ProductName NVARCHAR(100),5 ProductPrice DECIMAL(10,2)6);78CREATE 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
1-- Sales.SalesOrderDetail đạt 2NF2-- Thông tin Product được tách riêng3SELECT 4 sod.SalesOrderID,5 sod.SalesOrderDetailID,6 sod.ProductID,7 sod.OrderQty,8 sod.UnitPrice, -- Stored tại thời điểm order9 sod.LineTotal,10 p.Name AS ProductName -- Lấy từ table Product11FROM Sales.SalesOrderDetail sod12JOIN Production.Product p ON sod.ProductID = p.ProductID13WHERE sod.SalesOrderID = 43659;Checkpoint
Partial Dependency là gì? Tại sao nó chỉ xảy ra khi Primary Key là composite?
⚡ Third Normal Form (3NF)
Quy tắc 3NF
11. Đạt 2NF22. KHÔNG có Transitive Dependency3 - Non-key column KHÔNG được phụ thuộc vào non-key column khác4 - Mọi non-key phải phụ thuộc TRỰC TIẾP vào Primary KeyVí dụ: Vi phạm 3NF
1-- ❌ Vi phạm 3NF: Transitive Dependency2CREATE TABLE Employee (3 EmployeeID INT PRIMARY KEY,4 EmployeeName NVARCHAR(100),5 DepartmentID INT,6 DepartmentName NVARCHAR(100), -- Phụ thuộc DepartmentID7 DepartmentManager NVARCHAR(100) -- Phụ thuộc DepartmentID8);910-- Transitive: EmployeeID → DepartmentID → DepartmentNameFunctional Dependencies:
1EmployeeID → EmployeeName ✅ Direct2EmployeeID → DepartmentID ✅ Direct3DepartmentID → DepartmentName ❌ Transitive (non-key → non-key)4DepartmentID → DepartmentManager ❌ TransitiveChuyển sang 3NF
1-- ✅ Đạt 3NF: Tách Department ra table riêng2CREATE TABLE Department (3 DepartmentID INT PRIMARY KEY,4 DepartmentName NVARCHAR(100),5 DepartmentManager NVARCHAR(100)6);78CREATE 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
1-- HumanResources module đạt 3NF2-- Employee và Department được tách riêng34-- Employee table5SELECT 6 e.BusinessEntityID,7 p.FirstName,8 p.LastName,9 e.JobTitle,10 e.HireDate,11 d.Name AS DepartmentName, -- Từ Department table12 d.GroupName -- Từ Department table13FROM HumanResources.Employee e14JOIN Person.Person p ON e.BusinessEntityID = p.BusinessEntityID15JOIN HumanResources.EmployeeDepartmentHistory edh 16 ON e.BusinessEntityID = edh.BusinessEntityID17 AND edh.EndDate IS NULL18JOIN HumanResources.Department d ON edh.DepartmentID = d.DepartmentID19WHERE e.BusinessEntityID <= 10;Checkpoint
Transitive Dependency là gì? Hãy giải thích bằng ví dụ Employee → DepartmentID → DepartmentName.
🧠 Boyce-Codd Normal Form (BCNF)
Quy tắc BCNF
11. Đạt 3NF22. Mọi DETERMINANT phải là Candidate Key3 - Determinant: column(s) mà columns khác phụ thuộc vàoVí dụ: Vi phạm BCNF
1-- Scenario: 1 Student chỉ có 1 Advisor per Subject2-- 1 Advisor chỉ dạy 1 Subject3CREATE TABLE StudentAdvisor (4 StudentID INT,5 Subject VARCHAR(50),6 AdvisorID INT,7 PRIMARY KEY (StudentID, Subject)8);910-- Functional Dependencies:11-- StudentID, Subject → AdvisorID ✅ (PK determines non-key)12-- AdvisorID → Subject ❌ (non-key determines non-key)Chuyển sang BCNF
1-- ✅ Đạt BCNF2CREATE TABLE Advisor (3 AdvisorID INT PRIMARY KEY,4 Subject VARCHAR(50)5);67CREATE TABLE StudentAdvisor (8 StudentID INT,9 AdvisorID INT,10 PRIMARY KEY (StudentID, AdvisorID),11 FOREIGN KEY (AdvisorID) REFERENCES Advisor(AdvisorID)12);Checkpoint
BCNF khác 3NF ở điểm nào? Cho ví dụ một bảng đạt 3NF nhưng vi phạm BCNF.
🔧 Denormalization - Khi nào KHÔNG normalize?
Tại sao Denormalize?
1Performance > Purity| Normalize | Denormalize |
|---|---|
| Ít redundancy | Có redundancy |
| Nhiều JOINs | Ít JOINs |
| Insert/Update nhanh | SELECT nhanh |
| OLTP systems | OLAP/Reporting systems |
Ví dụ Denormalization
1-- ✅ Normalized (OLTP)2SELECT 3 c.CustomerName,4 o.OrderDate,5 p.ProductName,6 od.Quantity,7 od.UnitPrice8FROM Orders o9JOIN Customers c ON o.CustomerID = c.CustomerID10JOIN OrderDetails od ON o.OrderID = od.OrderID11JOIN Products p ON od.ProductID = p.ProductID;12-- 4 tables, 3 JOINs1314-- 📊 Denormalized for Reporting (OLAP)15CREATE TABLE SalesReport (16 OrderID INT,17 OrderDate DATE,18 CustomerID INT,19 CustomerName NVARCHAR(100), -- Denormalized20 ProductID INT,21 ProductName NVARCHAR(100), -- Denormalized22 Quantity INT,23 UnitPrice DECIMAL(10,2),24 TotalAmount DECIMAL(10,2) -- Pre-calculated25);26-- 1 table, 0 JOINsAdventureWorks Denormalization
1-- AdventureWorks có cả normalized và denormalized structures23-- Normalized: Production.Product + Production.ProductSubcategory + Production.ProductCategory4SELECT 5 p.ProductID,6 p.Name,7 ps.Name AS Subcategory,8 pc.Name AS Category9FROM Production.Product p10JOIN Production.ProductSubcategory ps ON p.ProductSubcategoryID = ps.ProductSubcategoryID11JOIN Production.ProductCategory pc ON ps.ProductCategoryID = pc.ProductCategoryID;1213-- Pre-calculated in SalesOrderDetail14SELECT 15 SalesOrderDetailID,16 OrderQty,17 UnitPrice,18 UnitPriceDiscount,19 LineTotal -- Pre-calculated: OrderQty * UnitPrice * (1 - UnitPriceDiscount)20FROM Sales.SalesOrderDetail21WHERE SalesOrderID = 43659;Checkpoint
Khi nào nên Denormalize thay vì Normalize? So sánh ưu nhược điểm của hai phương pháp.
🛠️ Hands-on: Normalize a Table
Exercise: Normalize này
1-- Unnormalized table2CREATE 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
Serviceskhông atomic (vi phạm 1NF)RoomType,RoomPricephụ thuộcRoomNumber(vi phạm 2NF nếu composite PK)GuestEmail,GuestPhonenên tách riêng
Step 2: Apply 1NF
1-- Tách Services ra2CREATE TABLE BookingService (3 BookingID INT,4 ServiceID INT,5 PRIMARY KEY (BookingID, ServiceID)6);Step 3: Apply 2NF & 3NF
1-- Guest table2CREATE TABLE Guest (3 GuestID INT PRIMARY KEY IDENTITY(1,1),4 GuestName NVARCHAR(100),5 GuestEmail NVARCHAR(100),6 GuestPhone NVARCHAR(20)7);89-- Room table10CREATE TABLE Room (11 RoomNumber INT PRIMARY KEY,12 RoomTypeID INT,13 FOREIGN KEY (RoomTypeID) REFERENCES RoomType(RoomTypeID)14);1516-- RoomType table17CREATE TABLE RoomType (18 RoomTypeID INT PRIMARY KEY IDENTITY(1,1),19 RoomTypeName NVARCHAR(50),20 BasePrice DECIMAL(10,2)21);2223-- Service table24CREATE TABLE Service (25 ServiceID INT PRIMARY KEY IDENTITY(1,1),26 ServiceName NVARCHAR(50),27 ServicePrice DECIMAL(10,2)28);2930-- Booking table31CREATE 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);4142-- 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);Checkpoint
Trong bài tập BookingInfo, column nào vi phạm 1NF? Cần tạo bao nhiêu bảng sau khi normalize hoàn toàn?
📝 Summary Checklist
1## Normalization Checklist2 3### 1NF ✅4- [ ] All columns have atomic values5- [ ] No repeating groups6- [ ] Each row is unique (has PK)7 8### 2NF ✅9- [ ] Is in 1NF10- [ ] No partial dependencies (all non-key columns depend on entire PK)11 12### 3NF ✅13- [ ] Is in 2NF14- [ ] No transitive dependencies (non-key doesn't depend on non-key)15 16### BCNF ✅17- [ ] Is in 3NF18- [ ] Every determinant is a candidate keyCheckpoint
Hãy tóm tắt yêu cầu chính của mỗi dạng chuẩn: 1NF, 2NF, 3NF và BCNF.
🚀 Bài 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!
