DE

[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

0 views
[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

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ã đơnNgày đặtTên khách hàngSĐT kháchĐịa chỉTên sản phẩmĐơn giáSố lượng
ĐH012026-09-01Nguyễn Văn A0901234567Hà NộiBàn phím cơ, Chuột không dây1.200.000, 450.0001, 2
ĐH022026-09-02Nguyễn Văn A0901234567Hà NộiTai nghe Gaming850.0001

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):

  1. 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.
  2. 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).
  3. 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:
    1. 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.
    2. 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ột Tên_khách_hàng chỉ phụ thuộc vào Mã_đơn, hoàn toàn không liên quan đến Mã_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)

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 (A→B→CA \rightarrow B \rightarrow C).
  • Phân tích lỗi: Trong bảng customers, ta có customer_id -> zip_code -> city_name. Cột city_name phụ thuộc vào zip_code, và zip_code phụ thuộc vào customer_id. Nếu lưu cả city_name và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ảng customers chỉ cần lưu zip_code.

3. Bảng tổng kết các dạng chuẩn hóa

Cấp độQuy tắc cốt lõiDị thường (Anomaly) được giải quyếtTriết lý dễ nhớ
1NFDữ 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, DELETE diễ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 JOIN giữ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.

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ép NULL hay 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)

  1. 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).
  2. 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.
  3. Luật bù trừ (Trade-off): Càng chuẩn hóa cao →\rightarrow Ghi càng nhanh, dữ liệu càng sạch →\rightarrow nhưng Truy vấn đọc phân tích (JOIN) càng tốn tài nguyên.
  4. 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).
0Claps