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 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
2. Các dạng chuẩn (Normal Forms)
Normalization Journey
1
Unnormalized
2
1NF
3
2NF
4
3NF
5
BCNF
| 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à đủ!
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 name33. Mỗi row là UNIQUE (có Primary Key)44. KHÔNG có repeating groupsVí dụ: Vi phạm 1NF
SQL
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
SQL
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
SQL
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;4. Second Normal Form (2NF)
Quy tắc 2NF
Text
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
SQL
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:
Text
1OrderID, ProductID → Quantity ✅ Full dependency2ProductID → ProductName ❌ Partial dependency3ProductID → ProductPrice ❌ Partial dependencyChuyển sang 2NF
SQL
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
SQL
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;5. Third Normal Form (3NF)
Quy tắc 3NF
Text
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
SQL
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:
Text
1EmployeeID → EmployeeName ✅ Direct2EmployeeID → DepartmentID ✅ Direct3DepartmentID → DepartmentName ❌ Transitive (non-key → non-key)4DepartmentID → DepartmentManager ❌ TransitiveChuyển sang 3NF
SQL
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
SQL
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;6. Boyce-Codd Normal Form (BCNF)
Quy tắc BCNF
Text
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
SQL
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
SQL
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);7. Denormalization - Khi nào KHÔNG normalize?
Tại sao Denormalize?
Text
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
SQL
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
SQL
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;8. Hands-on: Normalize a Table
Exercise: Normalize này
SQL
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
SQL
1-- Tách Services ra2CREATE TABLE BookingService (3 BookingID INT,4 ServiceID INT,5 PRIMARY KEY (BookingID, ServiceID)6);Step 3: Apply 2NF & 3NF
SQL
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);9. Summary Checklist
markdown
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 keyTiế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!
