Database Normalization (1NF, 2NF, 3NF)
Bạn sẽ học được gì
Đến cuối trang này, bạn sẽ có thể:
- Giải thích tại sao chuẩn hóa (Normalization) quan trọng — nó giải quyết những vấn đề gì
- Định nghĩa 1NF, 2NF, và 3NF với các yêu cầu chính xác
- Chuyển đổi một bảng chưa chuẩn hóa (Unnormalized Table) từng bước qua mỗi dạng chuẩn
- Nhận diện phụ thuộc hàm (Functional Dependencies) và phụ thuộc bắc cầu (Transitive Dependencies)
- Biết khi nào nên phản chuẩn hóa (Denormalize) để tối ưu hiệu suất
Vấn đề: Dữ liệu dư thừa và không nhất quán
Hãy tưởng tượng một bảng duy nhất lưu trữ mọi thứ về đơn hàng:
| order_id | customer_name | customer_email | product_names | quantities | prices |
|---|---|---|---|---|---|
| 1 | Alice | alice@mail.com | Laptop, Mouse | 1, 2 | 999, 25 |
| 2 | Alice | alice@mail.com | Keyboard | 1 | 79 |
Các vấn đề phát sinh:
- Bất thường cập nhật (Update Anomaly) — Email của Alice bị trùng lặp. Cập nhật ở một hàng nhưng không cập nhật ở hàng khác sẽ gây mất nhất quán.
- Bất thường thêm (Insert Anomaly) — Không thể thêm một khách hàng mới chưa từng đặt hàng.
- Bất thường xóa (Delete Anomaly) — Xóa đơn hàng 2 sẽ xóa bản ghi duy nhất của Alice, mất hoàn toàn thông tin liên hệ.
Chuẩn hóa (Normalization) là quá trình cấu trúc cơ sở dữ liệu để loại bỏ các bất thường này bằng cách tách bảng và thiết lập các mối quan hệ.
Các khái niệm cốt lõi
Phụ thuộc hàm (Functional Dependency)
Một phụ thuộc hàm tồn tại khi giá trị của một thuộc tính xác định giá trị của một thuộc tính khác.
- Ký hiệu:
A → Bnghĩa là "A xác định B" - V í dụ:
student_id → student_name(biết mã sinh viên sẽ xác định duy nhất tên sinh viên)
Phụ thuộc một phần (Partial Dependency)
Một phụ thuộc một phần xảy ra khi một thuộc tính không khóa phụ thuộc vào chỉ một phần của khóa chính tổng hợp (Composite Primary Key), không phải toàn bộ khóa.
Phụ thuộc bắc cầu (Transitive Dependency)
Một phụ thuộc bắc cầu xảy ra khi A → B và B → C, nghĩa là A → C gián tiếp. Thuộc tính không khóa C phụ thuộc vào một thuộc tính không khóa khác B thay vì trực tiếp vào khóa chính.
Dạng chuẩn thứ nhất (First Normal Form — 1NF)
Quy tắc
- Giá trị nguyên tử (Atomic Values) — mỗi ô chứa một giá trị đơn lẻ, không thể chia nhỏ (không có danh sách, mảng, hoặc giá trị phân cách bằng dấu phẩy)
- Không có nhóm lặp (No Repeating Groups) — mỗi cột có tên duy nhất; không có cột trùng lặp như
phone1,phone2 - Hàng duy nhất (Unique Rows) — mỗi hàng có thể phân biệt được (thường thông qua khóa chính)
Trước 1NF (vi phạm)
| order_id | customer_name | products |
|---|---|---|
| 1 | Alice | Laptop, Mouse, Keyboard |
| 2 | Bob | Monitor |
Vi phạm: products chứa nhiều giá trị trong một ô duy nhất.
Sau 1NF
| order_id | customer_name | product |
|---|---|---|
| 1 | Alice | Laptop |
| 1 | Alice | Mouse |
| 1 | Alice | Keyboard |
| 2 | Bob | Monitor |
Bây giờ mỗi ô đều là giá trị nguyên tử. Nhưng chúng ta đã tạo ra một vấn đề mới: customer_name lặp lại cho mỗi sản phẩm trong cùng một đơn hàng — đó là sự dư thừa, sẽ được giải quyết bởi 2NF.
Dạng chuẩn thứ hai (Second Normal Form — 2NF)
Quy tắc
- Phải đạt 1NF
- Không có phụ thuộc một phần (No Partial Dependencies) — mọi thuộc tính không khóa phải phụ thuộc vào toàn bộ khóa chính, không phải chỉ một phần
2NF chỉ áp dụng cho các bảng có khóa chính tổng hợp (Composite Primary Key). Nếu bảng của bạn có khóa chính là một cột duy nhất, nó tự động đạt 2NF (miễn là đã đạt 1NF).
Trước 2NF (vi phạm)
Xét một bảng theo dõi sinh viên tham gia khóa học nào, với giảng viên cho mỗi khóa:
| student_id | course_id | student_name | course_name | instructor |
|---|---|---|---|---|
| 1 | CS101 | Alice | Intro to CS | Prof. Smith |
| 1 | MATH200 | Alice | Calculus II | Prof. Jones |
| 2 | CS101 | Bob | Intro to CS | Prof. Smith |
Khóa chính tổng hợp: (student_id, course_id)
Các phụ thuộc:
(student_id, course_id) → instructor✅ phụ thuộc đầy đủstudent_id → student_name❌ phụ thuộc một phần — phụ thuộc vào chỉ một phần của khóacourse_id → course_name, instructor❌ phụ thuộc một phần — phụ thuộc vào chỉ một phần của khóa
Sau 2NF — Tách thành ba bảng
enrollments (bảng liên kết — Junction Table):
| student_id | course_id |
|---|---|
| 1 | CS101 |
| 1 | MATH200 |
| 2 | CS101 |
students:
| student_id | student_name |
|---|---|
| 1 | Alice |
| 2 | Bob |
courses:
| course_id | course_name | instructor |
|---|---|---|
| CS101 | Intro to CS | Prof. Smith |
| MATH200 | Calculus II | Prof. Jones |
Bây giờ mọi thuộc tính không khóa đều phụ thuộc vào toàn bộ khóa chính của bảng chứa nó.
Dạng chuẩn thứ ba (Third Normal Form — 3NF)
Quy tắc
- Phải đạt 2NF
- Không có phụ thuộc bắc cầu (No Transitive Dependencies) — các thuộc tính không khóa phải phụ thuộc vào khóa chính trực tiếp, không thông qua một thuộc tính không khóa khác
Trước 3NF (vi phạm)
| emp_id | emp_name | dept_id | dept_name | dept_location |
|---|---|---|---|---|
| 1 | Alice | D01 | Engineering | Floor 3 |
| 2 | Bob | D01 | Engineering | Floor 3 |
| 3 | Carol | D02 | Marketing | Floor 1 |
Các phụ thuộc:
emp_id → emp_name, dept_id, dept_name, dept_location✅dept_id → dept_name, dept_location❌ phụ thuộc bắc cầu- Nghĩa là:
emp_id → dept_id → dept_name, dept_location—dept_namevàdept_locationphụ thuộc vàodept_id, không trực tiếp vàoemp_id
Điều này gây ra: nếu Engineering chuyển sang Floor 5, bạn phải cập nhật mọi nhân viên trong phòng ban đó — bất thường cập nhật (Update Anomaly).
Sau 3NF — Tách thành hai bảng
employees:
| emp_id | emp_name | dept_id |
|---|---|---|
| 1 | Alice | D01 |
| 2 | Bob | D01 |
| 3 | Carol | D02 |
departments:
| dept_id | dept_name | dept_location |
|---|---|---|
| D01 | Engineering | Floor 3 |
| D02 | Marketing | Floor 1 |
Bây giờ dept_name và dept_location chỉ phụ thuộc vào dept_id (khóa chính của bảng departments), không phụ thuộc bắc cầu qua emp_id.
Tóm tắt: 1NF → 2NF → 3NF
| Dạng chuẩn | Yêu cầu | Loại bỏ |
|---|---|---|
| 1NF | Giá trị nguyên tử, không có nhóm lặp | Ô đa giá trị, cột trùng lặp |
| 2NF | 1NF + không có phụ thuộc một phần | Dư thừa từ tập con của khóa tổng hợp |
| 3NF | 2NF + không có phụ thuộc bắc cầu | Dư thừa từ phụ thuộc không khóa → không khóa |
Cách kiểm tra nhanh cho mỗi dạng chuẩn:
- 1NF: Mỗi ô có phải là một giá trị duy nhất không? Có nhóm lặp nào không?
- 2NF: Mọi cột không khóa có phụ thuộc vào toàn bộ khóa chính không? (Chỉ liên quan đến khóa tổng hợp)
- 3NF: Mọi cột không khóa có phụ thuộc vào khóa chính trực tiếp không, không thông qua cột không khóa khác?
Ví dụ đầy đủ: Chuyển đổi từng bước
Bảng chưa chuẩn hóa (Unnormalized)
| order_id | date | customer_id | customer_name | customer_city | product_id | product_name | quantity | unit_price |
|---|---|---|---|---|---|---|---|---|
| 1001 | 2024-01-15 | C50 | Alice | Hanoi | P01 | Laptop | 1 | 999 |
| 1001 | 2024-01-15 | C50 | Alice | Hanoi | P02 | Mouse | 2 | 25 |
| 1002 | 2024-01-16 | C51 | Bob | Saigon | P01 | Laptop | 1 | 999 |
Sau 1NF — đã nguyên tử ✅
Mỗi ô là một giá trị duy nhất. Không có nhóm lặp.
Sau 2NF — kiểm tra phụ thuộc một phần
Khóa chính: (order_id, product_id) — khóa tổng hợp.
order_id → date, customer_id, customer_name, customer_city❌ phụ thuộc một phần — tách raordersproduct_id → product_name, unit_price❌ phụ thuộc một phần — tách raproducts(order_id, product_id) → quantity✅ phụ thuộc đầy đủ — giữ trongorder_items
Kết quả: orders, order_items, products
Sau 3NF — kiểm tra phụ thuộc bắc cầu
Trong bảng orders: customer_id → customer_name, customer_city — customer_name và customer_city phụ thuộc vào customer_id, không trực tiếp vào order_id. Đây là phụ thuộc bắc cầu.
Kết quả: tách orders thành orders và customers.
Schema đã chuẩn hóa hoàn chỉnh (3NF)
customers:
| customer_id | customer_name | customer_city |
|---|---|---|
| C50 | Alice | Hanoi |
| C51 | Bob | Saigon |
orders:
| order_id | order_date | customer_id |
|---|---|---|
| 1001 | 2024-01-15 | C50 |
| 1002 | 2024-01-16 | C51 |
products:
| product_id | product_name | unit_price |
|---|---|---|
| P01 | Laptop | 999 |
| P02 | Mouse | 25 |
order_items:
| order_id | product_id | quantity |
|---|---|---|
| 1001 | P01 | 1 |
| 1001 | P02 | 2 |
| 1002 | P01 | 1 |
Khi nào nên phản chuẩn hóa (Denormalize)
Schema đã chuẩn hóa giảm thiểu dư thừa nhưng có thể làm tăng độ phức tạp JOIN và độ trễ truy vấn (Query Latency). Trong thực tế, phản chuẩn hóa thường được sử dụng cho:
| Tình huống | Cách xử lý |
|---|---|
| Hệ thống đọc nhiều (Read-Heavy) | Nhân đôi các cột thường xuyên truy cập để tránh JOINs |
| Báo cáo / OLAP | Tổng hợp trước dữ liệu vào bảng tóm tắt hoặc Materialized Views |
| JOIN có độ trễ cao | Nhúng dữ liệu liên quan (ví dụ: lưu customer_name trong orders để hiển thị nhanh) |
| Cơ sở dữ liệu phân tán (Distributed Databases) | Đặt cùng vị trí dữ liệu liên quan để giảm truy vấn xuyên节点 |
Chuẩn hóa trước, phản chuẩn hóa có chủ đích. Bắt đầu với 3NF làm cơ sở. Chỉ phản chuẩn hóa khi bạn có một vấn đề hiệu suất có thể đo lường được và lợi ích rõ ràng.
Các sai lầm thường gặp
- Dừng lại ở 1NF — ô đa giá trị "hoạt động" ban đầu nhưng gây khó khăn khi phân tích và ngăn việc lập chỉ mục (Indexing)
- Chuẩn hóa quá mức (Over-normalizing) — tách thành quá nhiều bảng nhỏ làm truy vấn trở nên phức tạp với quá nhiều JOINs
- Quên lập chỉ mục khóa ngoại (Foreign Key) — schema đã chuẩn hóa phụ thuộc nhiều vào JOINs; thiếu index trên khóa ngoại sẽ làm giảm hiệu suất nghiêm trọng
- Phản chuẩn hóa quá sớm — phản chuẩn hóa trước khi cần sẽ tạo ra chính các bất thường mà chuẩn hóa được thiết kế để loại bỏ
- Bỏ qua quy tắc nghiệp vụ — chuẩn hóa là công cụ cấu trúc; các quy tắc nghiệp vụ phức tạp vẫn có thể cần xác thực ở cấp ứng dụng
Câu hỏi phỏng vấn
1. Chuẩn hóa cơ sở dữ liệu (Database Normalization) là gì và tại sao nó quan trọng?
Chuẩn hóa là quá trình tổ chức cơ sở dữ liệu thành các bảng và cột để giảm thiểu dư thừa và loại bỏ các bất thường dữ liệu (Data Anomalies) (thêm, cập nhật, xóa). Nó đảm bảo mỗi phần dữ liệu được lưu trữ ở đúng một nơi, giúp cơ sở dữ liệu dễ bảo trì và nhất quán hơn.
2. Giải thích 1NF, 2NF, và 3NF một cách đơn giản.
| Dạng chuẩn | Diễn giải đơn giản |
|---|---|
| 1NF | Một giá trị trên mỗi ô. Không có danh sách hoặc giá trị phân cách bằng dấu phẩy trong cột. |
| 2NF | Mọi cột không khóa phụ thuộc vào toàn bộ khóa chính. (Không có phụ thuộc một phần.) |
| 3NF | Mọi cột không khóa phụ thuộc chỉ vào khóa chính. (Không có phụ thuộc bắc cầu.) |
Một câu nhớ: "Mọi thuộc tính không khóa phải cung cấp thông tin về khóa (1NF), toàn bộ khóa (2NF), và chỉ về khóa mà thôi (3NF)."
3. Sự khác biệt giữa phụ thuộc một phần (Partial Dependency) và phụ thuộc bắc cầu (Transitive Dependency) là gì?
- Phụ thuộc một phần (Partial Dependency): một thuộc tính không khóa phụ thuộc vào một phần của khóa tổng hợp (ví dụ:
student_namechỉ phụ thuộc vàostudent_id, không phải(student_id, course_id)). Chỉ liên quan đến khóa chính tổng hợp. - Phụ thuộc bắc cầu (Transitive Dependency): một thuộc tính không khóa phụ thuộc vào một thuộc tính không khóa khác (ví dụ:
dept_namephụ thuộc vàodept_id, màdept_idphụ thuộc vàoemp_id). Có thể tồn tại ngay cả với khóa chính là một cột duy nhất.
4. Một bảng có khóa chính là một cột duy nhất có tự động đạt 2NF không?
Có. 2NF yêu cầu loại bỏ các phụ thuộc một phần — khi một cột không khóa chỉ phụ thuộc vào một phần của khóa chính. Nếu khóa chính là một cột duy nhất, không có "phần của nó" để phụ thuộc một phần, nên không thể tồn tại phụ thuộc một phần.
5. Khi nào bạn nên phản chuẩn hóa (Denormalize) cơ sở dữ liệu?
Phản chuẩn hóa khi hiệu suất truy vấn quan trọng hơn hiệu quả lưu trữ:
- Hệ thống đọc nhiều nơi JOINs là điểm nghẽn
- Dashboard báo cáo và phân tích cần dữ liệu được tổng hợp trước
- Kiến trúc CQRS nơi mô hình đọc (Read Model) tách biệt với mô hình ghi (Write Model)
- Cơ sở dữ liệu phân tán (Distributed Databases) nơi JOINs xuyên node rất tốn kém
Luôn chuẩn hóa trước, sau đó phản chuẩn hóa với mục tiêu hiệu suất cụ thể.
6. Các bất thường (Anomalies) nào mà chuẩn hóa ngăn ngừa?
| Bất thường | Mô tả | Ví dụ |
|---|---|---|
| Bất thường thêm (Insert Anomaly) | Không thể thêm bản ghi mà không có dữ liệu không liên quan | Không thể thêm phòng ban mới cho đến khi có nhân viên được phân công |
| Bất thường cập nhật (Update Anomaly) | Cập nhật một giá trị yêu cầu cập nhật nhiều hàng | Thay đổi email khách hàng yêu cầu cập nhật mọi hàng đơn hàng |
| Bất thường xóa (Delete Anomaly) | Xóa một bản ghi vô tình làm mất dữ liệu khác | Xóa đơn hàng cuối cùng của khách hàng sẽ xóa hoàn toàn thông tin khách hàng |