[Tech Blog #04 | Data Engineering] Thiết Kế CSDL Quan Hệ & Chuẩn Hóa Dữ Liệu (1NF, 2NF, 3NF): Bí Quyết Tránh "Thảm Họa Dữ Liệu" Trong Mọi Hệ Thống
![[Tech Blog #04 | Data Engineering] Thiết Kế CSDL Quan Hệ & Chuẩn Hóa Dữ Liệu (1NF, 2NF, 3NF): Bí Quyết Tránh "Thảm Họa Dữ Liệu" Trong Mọi Hệ Thống](/uploads/ai-images/cover-thiet-ke-chuan-hoa-csdl.png)
1. Đặt vấn đề: Thảm họa của bảng dữ liệu "nhét chung tất cả"
Khi mới bắt đầu làm việc với dữ liệu, nhiều người có thói quen đưa tất cả thông tin vào một bảng tính duy nhất (như một file Excel khổng lồ):
| Mã đơn | Ngày đặt | Tên khách hàng | SĐT khách | Địa chỉ | Tên sản phẩm | Đơn giá | Số lượng |
|---|---|---|---|---|---|---|---|
| ĐH01 | 2026-09-01 | Nguyễn Văn A | 0901234567 | Hà Nội | Bàn phím cơ, Chuột không dây | 1.200.000, 450.000 | 1, 2 |
| ĐH02 | 2026-09-02 | Nguyễn Văn A | 0901234567 | Hà Nội | Tai nghe Gaming | 850.000 | 1 |
Nhìn qua thì có vẻ tiện lợi vì chỉ cần mở 1 bảng là thấy hết. Nhưng trong môi trường sản xuất (Production Database), thiết kế này sẽ dẫn đến 3 "thảm họa dữ liệu" kinh điển (Anomalies):
- Dị thường cập nhật (Update Anomaly): Nếu khách hàng Nguyễn Văn A đổi số điện thoại, bạn phải chạy lệnh cập nhật trên hàng chục, thậm chí hàng nghìn dòng đơn hàng cũ của anh ấy. Nếu sót dù chỉ 1 dòng, dữ liệu sẽ mâu thuẫn ngay lập tức.
- Dị thường chèn (Insertion Anomaly): Bạn nhập một sản phẩm mới về kho nhưng chưa có ai mua. Vì không có
Mã đơn, bạn không thể chèn dòng dữ liệu này vào bảng (trừ khi chấp nhận để các cột đơn hàng làNULL). - Dị thường xóa (Deletion Anomaly): Nếu khách hàng hủy đơn hàng duy nhất chứa một sản phẩm đặc biệt, bạn xóa dòng đó đi và vô tình làm mất luôn thông tin giá bán và tên của sản phẩm đó trong hệ thống.
Chuẩn hóa dữ liệu (Database Normalization) chính là quy trình chia tách các bảng lớn thành các bảng nhỏ hơn có mối liên kết chặt chẽ, nhằm triệt tiêu hoàn toàn sự trùng lặp và bảo vệ tính toàn vẹn dữ liệu.
2. Hành trình chuẩn hóa: Từ 0 lên 3NF
Có một câu thần chú nổi tiếng trong ngành công nghệ dữ liệu tóm tắt trọn vẹn 3 dạng chuẩn hóa đầu tiên:
"Every non-key attribute must depend on the key, the whole key, and nothing but the key."
(Mọi thuộc tính không khóa phải phụ thuộc vào khóa chính, toàn bộ khóa chính, và không gì khác ngoài khóa chính.)
2.1. Dạng chuẩn 1 (1NF - First Normal Form): Tính nguyên tử (Atomic Values)
- Quy tắc:
- Mỗi ô (cell) chỉ được chứa đúng 1 giá trị đơn lẻ, không được chứa danh sách, chuỗi ngăn cách bởi dấu phẩy, hoặc mảng JSON.
- Mỗi bảng phải có một Khóa chính (Primary Key) để định danh từng dòng duy nhất.
- Cách sửa ví dụ trên: Tách dòng ĐH01 có 2 sản phẩm thành 2 dòng riêng biệt, mỗi dòng chứa duy nhất 1 sản phẩm.
2.2. Dạng chuẩn 2 (2NF - Second Normal Form): Loại bỏ phụ thuộc một phần (Partial Dependency)
- Điều kiện: Đã đạt 1NF.
- Quy tắc: Mọi cột không phải khóa chính phải phụ thuộc vào toàn bộ khóa chính, chứ không được chỉ phụ thuộc vào một phần của khóa chính kết hợp (Composite Key).
- Phân tích lỗi: Giả sử bảng bán hàng có khóa chính kết hợp là
(Mã_đơn, Mã_sản_phẩm). CộtTên_khách_hàngchỉ phụ thuộc vàoMã_đơn, hoàn toàn không liên quan đếnMã_sản_phẩm. - Cách sửa: Tách thành các bảng riêng:
- Bảng Đơn hàng:
orders (order_id, customer_id, order_date) - Bảng Chi tiết đơn:
order_items (order_id, product_id, quantity) - Bảng Sản phẩm:
products (product_id, product_name, unit_price)
- Bảng Đơn hàng:
2.3. Dạng chuẩn 3 (3NF - Third Normal Form): Loại bỏ phụ thuộc bắc cầu (Transitive Dependency)
- Điều kiện: Đã đạt 2NF.
- Quy tắc: Các cột không phải khóa chính không được phụ thuộc lẫn nhau ().
- Phân tích lỗi: Trong bảng
customers, ta cócustomer_id -> zip_code -> city_name. Cộtcity_namephụ thuộc vàozip_code, vàzip_codephụ thuộc vàocustomer_id. Nếu lưu cảcity_namevào bảng khách hàng thì thông tin thành phố sẽ bị lặp lại hàng triệu lần. - Cách sửa: Tách bảng địa phương riêng:
zip_codes (zip_code, city_name). Bảngcustomerschỉ cần lưuzip_code.
3. Bảng tổng kết các dạng chuẩn hóa
| Cấp độ | Quy tắc cốt lõi | Dị thường (Anomaly) được giải quyết | Triết lý dễ nhớ |
|---|---|---|---|
| 1NF | Dữ liệu nguyên tử, không có cột đa trị hoặc danh sách lặp. | Loại bỏ sự mơ hồ khi tìm kiếm và lọc dữ liệu bên trong chuỗi. | "The Key" (Có khóa chính, giá trị đơn lẻ) |
| 2NF | Đạt 1NF + Không có phụ thuộc một phần vào khóa kết hợp. | Tránh lặp lại thông tin chung của đơn hàng cho từng món đồ. | "The Whole Key" (Phụ thuộc toàn bộ khóa) |
| 3NF | Đạt 2NF + Không có phụ thuộc bắc cầu giữa các cột không khóa. | Triệt tiêu trùng lặp gián tiếp (ví dụ: tỉnh/thành phố, chức vụ công ty). | "Nothing but the Key" (Chỉ phụ thuộc khóa chính) |
4. Thực hành thực chiến: Khởi tạo Schema chuẩn 3NF với PostgreSQL
Dưới đây là đoạn mã SQL DDL (Data Definition Language) thiết kế cơ sở dữ liệu bán hàng chuẩn 3NF với các ràng buộc khóa chính (PK), khóa ngoại (FK) và tính toàn vẹn dữ liệu:
-- 1. Bảng Khách hàng (Lưu trữ độc lập thông tin người dùng)
CREATE TABLE customers (
customer_id SERIAL PRIMARY KEY, -- Khóa chính tự tăng
full_name VARCHAR(100) NOT NULL, -- Tên không được rỗng
email VARCHAR(100) UNIQUE NOT NULL, -- Email duy nhất, không trùng lặp
phone VARCHAR(20),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 2. Bảng Sản phẩm (Quản lý hàng hóa độc lập, giải quyết Insertion Anomaly)
CREATE TABLE products (
product_id SERIAL PRIMARY KEY, -- Khóa chính
product_name VARCHAR(150) NOT NULL,
category VARCHAR(50) NOT NULL,
unit_price NUMERIC(12, 2) NOT NULL CHECK (unit_price >= 0), -- Ràng buộc giá không âm
stock_quantity INT NOT NULL DEFAULT 0 CHECK (stock_quantity >= 0)
);
-- 3. Bảng Đơn hàng (Lưu thông tin tổng quan của mỗi giao dịch)
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT NOT NULL, -- Khóa ngoại trỏ về customers
order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status VARCHAR(20) DEFAULT 'PENDING' CHECK (status IN ('PENDING', 'PAID', 'SHIPPED', 'CANCELLED')),
-- Thiết lập ràng buộc toàn vẹn tham chiếu
CONSTRAINT fk_orders_customer
FOREIGN KEY (customer_id)
REFERENCES customers(customer_id)
ON DELETE RESTRICT -- Không cho phép xóa khách hàng nếu đang có đơn hàng
);
-- 4. Bảng Chi tiết đơn hàng (Cầu nối nhiều-nhiều giữa Orders và Products, đạt chuẩn 3NF)
CREATE TABLE order_items (
order_id INT NOT NULL,
product_id INT NOT NULL,
quantity INT NOT NULL CHECK (quantity > 0),
price_at_purchase NUMERIC(12, 2) NOT NULL, -- Giá tại thời điểm mua (bảo vệ lịch sử giá)
-- Khóa chính kết hợp (Composite Primary Key)
PRIMARY KEY (order_id, product_id),
-- Ràng buộc khóa ngoại
CONSTRAINT fk_items_order
FOREIGN KEY (order_id)
REFERENCES orders(order_id)
ON DELETE CASCADE, -- Xóa đơn hàng thì tự động xóa các dòng chi tiết
CONSTRAINT fk_items_product
FOREIGN KEY (product_id)
REFERENCES products(product_id)
ON DELETE RESTRICT
);
5. Góc nhìn Data Engineer: Chuẩn hóa (OLTP) vs. Phi chuẩn hóa (OLAP)
Là một kỹ sư dữ liệu, bạn sẽ nhận ra một nghịch lý thú vị:
- Hệ thống OLTP (Online Transaction Processing - App/Web chạy hàng ngày): Cần chuẩn hóa tối đa đến 3NF để việc
INSERT,UPDATE,DELETEdiễn ra nhanh như chớp, không tốn bộ nhớ lưu trùng lặp và không bao giờ sợ sai lệch dữ liệu giao dịch. - Hệ thống OLAP (Online Analytical Processing - Data Warehouse như ClickHouse, BigQuery, Snowflake): Lại chủ động Phi chuẩn hóa (Denormalization)!
- Lý do: Khi phân tích hàng trăm triệu dòng, các phép
JOINgiữa 7–8 bảng chuẩn hóa 3NF sẽ tiêu tốn lượng CPU và RAM khổng lồ, làm chậm tốc độ báo cáo. - Giải pháp: Tại Data Warehouse, chúng ta gộp sẵn dữ liệu thành mô hình Star Schema (bảng Fact ở giữa kết nối các bảng Dimension xung quanh) hoặc một bảng rộng (Wide Table) để truy vấn báo cáo nhanh nhất.
- Lý do: Khi phân tích hàng trăm triệu dòng, các phép
6. Câu hỏi phỏng vấn thực tế (Interview Q&A)
Câu 1 (Mức độ: Junior / Entry-level)
Câu hỏi: "Sự khác biệt cơ bản giữa Primary Key (Khóa chính) và Foreign Key (Khóa ngoại) là gì?"
Gợi ý trả lời:
"Primary Key là cột (hoặc cụm cột) dùng để định danh duy nhất cho từng bản ghi trong bảng, bắt buộc phải có giá trị và không được phépNULLhay trùng lặp.
Ngược lại, Foreign Key là cột trong một bảng dùng để liên kết đến Primary Key của một bảng khác. Nó thiết lập mối quan hệ giữa các thực thể và đảm bảo tính toàn vẹn tham chiếu (Referential Integrity), ngăn chặn trường hợp tạo đơn hàng cho một khách hàng không tồn tại."
Câu 2 (Mức độ: Junior / Mid-level)
Câu hỏi: "Tại sao trong Data Engineering chúng ta lại cần chuyển đổi từ mô hình 3NF ở nguồn sang mô hình Dimensional Modeling (Star Schema) trong Data Warehouse?"
Gợi ý trả lời:
"Cơ sở dữ liệu nguồn (OLTP) được tối ưu hóa cho việc ghi (Write-heavy) với các thao tác CRUD liên tục, nên chuẩn hóa 3NF là tối ưu để tránh trùng lặp và khóa bảng (locking).
Tuy nhiên, Data Warehouse phục vụ cho việc đọc và phân tích dữ liệu lớn (Read-heavy). Nếu giữ nguyên cấu trúc 3NF, các truy vấn BI/Dashboard sẽ phải JOIN qua quá nhiều bảng nhỏ, gây suy giảm hiệu năng nghiêm trọng. Việc chuyển sang Star Schema (với Fact table chứa các chỉ số đo lường và Dimension tables chứa các chiều lọc/phân tích) giúp đơn giản hóa cú pháp truy vấn cho Data Analyst và tăng tốc độ quét dữ liệu dạng cột (Columnar Storage)."
7. Key Takeaways (Ghi nhớ nhanh)
- Chuẩn hóa sinh ra để cứu hệ thống khỏi 3 dị thường: Chèn (Insert), Cập nhật (Update), và Xóa (Delete).
- Quy tắc 3 bước:
- 1NF: Không chứa mảng/danh sách trong ô, dữ liệu nguyên tử.
- 2NF: Bỏ phụ thuộc một phần vào khóa kết hợp.
- 3NF: Bỏ phụ thuộc bắc cầu giữa các thuộc tính không khóa.
- Luật bù trừ (Trade-off): Càng chuẩn hóa cao Ghi càng nhanh, dữ liệu càng sạch nhưng Truy vấn đọc phân tích (JOIN) càng tốn tài nguyên.
- Quy tắc vàng của Data Engineer: Chuẩn hóa ở nguồn giao dịch (OLTP), phi chuẩn hóa ở kho dữ liệu phân tích (OLAP/Data Warehouse).