1. Database Normalization là gì?

Database Normalization (chuẩn hóa dữ liệu) là quá trình tổ chức dữ liệu thành các bảng và quan hệ hợp lý nhằm giảm dư thừa không cần thiết, tránh dữ liệu mâu thuẫn và duy trì tính toàn vẹn của database.

Khi thiết kế database, nếu lưu tất cả thông tin vào một bảng lớn, ban đầu việc truy vấn có thể có vẻ đơn giản. Nhưng khi dữ liệu tăng lên, một thông tin có thể bị lặp lại ở nhiều dòng, khiến việc cập nhật trở nên phức tạp và dễ phát sinh lỗi.

Ví dụ, một cửa hàng lưu đơn hàng cùng tên khách hàng và tên sản phẩm trong cùng một bảng. Khi một khách hàng tạo nhiều đơn hàng, tên và email của họ sẽ bị lặp lại. Nếu email thay đổi mà chỉ một số dòng được cập nhật, database sẽ chứa dữ liệu không nhất quán.

Normalization giải quyết vấn đề này bằng cách xác định mỗi thông tin thực sự thuộc về đâu, sau đó phân chia dữ liệu thành các bảng với quan hệ rõ ràng.

Ba dạng chuẩn thường cần nắm khi thiết kế relational database là:

  • 1NF (First Normal Form): Mỗi trường chứa một giá trị phù hợp với miền dữ liệu, không tổ chức các nhóm giá trị lặp lại trong cùng một cấu trúc.
  • 2NF (Second Normal Form): Đạt 1NF và loại bỏ sự phụ thuộc một phần vào khóa ghép.
  • 3NF (Third Normal Form): Đạt 2NF và loại bỏ những phụ thuộc không khóa gây dư thừa, thường được giải thích thông qua phụ thuộc bắc cầu.

2. Vì sao cần Normalization?

Để hiểu các dạng chuẩn, trước tiên cần hiểu những vấn đề xảy ra khi dữ liệu bị tổ chức không hợp lý.

Data Redundancy – Dữ liệu dư thừa

Giả sử bảng lưu đơn hàng có cấu trúc:

OrderID CustomerName CustomerEmail ProductName
1001 John [email protected] Keyboard
1001 John [email protected] Mouse
1002 John [email protected] Monitor

Tên và email của John xuất hiện nhiều lần. Nếu khách hàng có hàng trăm đơn hàng, cùng một thông tin sẽ được lưu lặp lại rất nhiều.

Update Anomaly

Giả sử John đổi email. Nếu application chỉ cập nhật một số dòng mà bỏ sót những dòng khác, database có thể chứa nhiều email khác nhau cho cùng một khách hàng.

Insert Anomaly

Nếu thông tin sản phẩm chỉ được lưu trong bảng order, có thể ứng dụng không lưu được sản phẩm mới khi chưa có đơn hàng nào chứa sản phẩm đó.

Delete Anomaly

Nếu khách hàng và sản phẩm chỉ xuất hiện trong các dòng đơn hàng, xóa dòng order cuối cùng có thể vô tình làm mất thông tin duy nhất đang lưu về một sản phẩm hoặc khách hàng.

Đây là delete anomaly: thao tác xóa một loại dữ liệu khiến một loại thông tin khác cũng bị mất ngoài ý muốn.

3. First Normal Form (1NF) – Mỗi trường chứa một giá trị phù hợp

1NF là dạng chuẩn đầu tiên của relational database. Theo cách học thực tế, một bảng nên có các hàng và cột rõ ràng, mỗi ô chứa một giá trị thuộc miền dữ liệu được xác định, và không tổ chức những nhóm cột lặp lại để chứa nhiều giá trị cùng loại.

Hãy xét một bảng lưu đơn hàng chưa được chuẩn hóa:

orderID               CustomerName                            Products

1001                    john                                                 keyboard, mouse

1002                    john                                                 monitor,webcam
Vấn đề nằm ở column Products. Nó chứa nhiều sản phẩm trong cùng một ô. Database khó thực hiện những truy vấn có cấu trúc như tìm tất cả order chứa Mouse, đếm số lượng từng sản phẩm hoặc thiết lập foreign key đến bảng sản phẩm.

Một thiết kế khác cũng không hợp lý là dùng các cột cố định như:

OrderID
Product1
Product2
Product3
Cách này giới hạn số lượng sản phẩm và buộc schema phải thay đổi khi có nhiều trường hợp hơn.

4. Second Normal Form (2NF) – Phụ thuộc vào toàn bộ khóa

2NF yêu cầu bảng đã đạt 1NF và các thuộc tính không thuộc khóa phải phụ thuộc đầy đủ vào khóa, không chỉ phụ thuộc vào một phần của khóa ghép.

Điểm này thường gây nhầm lẫn: vấn đề của 2NF xuất hiện rõ nhất khi bảng sử dụng khóa ghép, tức primary key hoặc candidate key gồm nhiều thuộc tính.

Giả sử mỗi sản phẩm chỉ xuất hiện tối đa một dòng trong một order. Khi đó, khóa của bảng chi tiết là:

[
(OrderID, ProductID)
]

Ta phân tích sự phụ thuộc của từng thuộc tính:

OrderDate chỉ phụ thuộc vào OrderID, vì một order có một ngày tạo.
ProductName chỉ phụ thuộc vào ProductID, vì tên mô tả sản phẩm.
Quantity phụ thuộc vào cả OrderID và ProductID, vì số lượng là thuộc tính của một sản phẩm cụ thể trong một order.
Hai thuộc tính đầu chỉ phụ thuộc vào một phần của khóa ghép. Đây gọi là Partial Dependency (phụ thuộc một phần).

Nếu giữ bảng như cũ, ngày order bị lặp theo từng sản phẩm, còn tên sản phẩm lặp theo từng order. Bảng chứa thông tin thuộc về những entity khác nhau.

Chuyển từ 1NF sang 2NF

Tách bảng thành ba phần:

Orders lưu thông tin của mỗi order:

Orders
– OrderID (PK)
– OrderDate
Products lưu thông tin của mỗi sản phẩm:

Products
– ProductID (PK)
– ProductName
OrderItems lưu quan hệ giữa order và sản phẩm:

OrderItems
– OrderID (PK, FK)
– ProductID (PK, FK)
– Quantity
Ở đây, khóa ghép (OrderID, ProductID) của OrderItems xác định Quantity. Ngày tạo được lưu một lần ở Orders, còn tên sản phẩm được lưu một lần ở Products.

5. Third Normal Form (3NF) – Loại bỏ phụ thuộc bắc cầu không phù hợp

3NF yêu cầu bảng đạt 2NF và tránh những phụ thuộc không khóa khiến dữ liệu của một entity bị lưu lặp qua entity khác.

Một cách giải thích thường dùng là: các thuộc tính không khóa nên phụ thuộc vào khóa, toàn bộ khóa và không phụ thuộc thông qua một thuộc tính không khóa khác.

Giả sử OrderID là primary key. Ta có các phụ thuộc:

[
OrderID –> CustomerID
]

[
CustomerID –> CustomerName, CustomerEmail
]

Từ đó suy ra:

[
OrderID –> CustomerName, CustomerEmail
]

Nhưng tên và email không thực sự là thuộc tính của order. Chúng là thuộc tính của customer. Order chỉ lưu CustomerID để tham chiếu đến customer.

Đây là Transitive Dependency (phụ thuộc bắc cầu): OrderID xác định thông tin customer thông qua CustomerID.

Chuyển từ 2NF sang 3NF
Tách thông tin thành hai bảng:

Customers
– CustomerID (PK)
– CustomerName
– CustomerEmail

Orders
– OrderID (PK)
– OrderDate
– CustomerID (FK)
Giờ đây, tên và email được lưu ở Customers, còn Orders chỉ giữ khóa tham chiếu đến customer.

Khi John đổi email, database chỉ cần cập nhật thông tin ở bảng Customers, thay vì phải sửa từng order. Các order cũ vẫn liên kết với đúng customer thông qua CustomerID.

6. Denormalization – Khi chủ động lưu dữ liệu dư thừa

Denormalization (phi chuẩn hóa) là việc chủ động bổ sung hoặc giữ lại dữ liệu dư thừa có kiểm soát để phục vụ yêu cầu truy vấn, hiệu năng hoặc cách sử dụng dữ liệu của hệ thống.

Normalization ưu tiên cấu trúc và tính toàn vẹn của dữ liệu. Denormalization chấp nhận một số redundancy để giảm công việc cần thực hiện khi đọc hoặc tổng hợp dữ liệu.

Ví dụ, khi truy vấn một danh sách order, application có thể cần thông tin customer, order và các item. Nếu cấu trúc đã chuẩn hóa, dữ liệu có thể phải được ghép từ nhiều bảng bằng JOIN.

Ví dụ 1: Lưu tổng tiền đơn hàng

Giả sử OrderItems chứa giá và số lượng của từng sản phẩm. Tổng tiền cơ bản có thể được tính:

[
Total = \sum (Quantity \times UnitPrice)
]

Nếu mỗi lần hiển thị order đều tính lại tổng từ các item, application phải thực hiện phép tổng hợp. Một số hệ thống lưu sẵn orders.total_amount để đọc nhanh hơn hoặc giữ giá trị đã được chốt theo nghiệp vụ.

Khi nào nên Denormalize?

Denormalization thường chỉ nên xem xét khi có nhu cầu cụ thể, chẳng hạn query tổng hợp đang quá tốn kém, read workload rất lớn hoặc hệ thống cần một read model tối ưu cho giao diện và báo cáo.

Trước khi phi chuẩn hóa, nên đo query thực tế, đọc execution plan, xem index và đánh giá việc tối ưu SQL. Nếu truy vấn đã đủ nhanh, thêm dữ liệu dư thừa chỉ tăng độ phức tạp mà không mang lại lợi ích đáng kể.

Khi thực hiện Denormalization, cần trả lời ba câu hỏi: dữ liệu dư thừa được tạo ra để làm gì, khi dữ liệu nguồn thay đổi thì giá trị dư thừa được cập nhật như thế nào và application sẽ phát hiện hoặc khôi phục sai lệch ra sao.

7. Tổng kết

Database Normalization giúp tổ chức dữ liệu sao cho mỗi thông tin được lưu ở nơi phù hợp, giảm dư thừa gây lỗi và duy trì tính nhất quán.

1NF tập trung vào cách biểu diễn các giá trị và loại bỏ nhóm dữ liệu lặp không phù hợp. 2NF loại bỏ phụ thuộc một phần khi khóa có nhiều thuộc tính. 3NF xử lý những phụ thuộc không khóa, thường gặp nhất là trường hợp một thuộc tính không khóa được xác định thông qua thuộc tính không khóa khác.

BCNF là dạng chuẩn chặt chẽ hơn, hữu ích khi các quan hệ giữa candidate key và functional dependency phức tạp. Denormalization có mục tiêu khác: chủ động duy trì một số dữ liệu dư thừa để đáp ứng yêu cầu đọc, tổng hợp hoặc snapshot nghiệp vụ.

Bài viết khác

Database Design & Relationships

1. Database Design là gì? Database Design là quá trình thiết kế cách tổ chức, lưu trữ và liên kết dữ liệu trong một hệ thống sao cho dữ liệu có cấu trúc hợp lý, hạn chế trùng lặp không cần thiết, bảo đảm tính toàn vẹn và hỗ trợ các truy vấn mà application […]

PostgreSQL / MySQL

1. PostgreSQL và MySQL là gì? PostgreSQL và MySQL là hai hệ quản trị cơ sở dữ liệu quan hệ (Relational Database Management System – RDBMS), được sử dụng để lưu trữ, truy vấn và quản lý dữ liệu cho các ứng dụng. Tuy cùng thuộc nhóm relational database, PostgreSQL và MySQL có sự khác […]

Sanity Testing(Kiểm thử sự hợp lý)

Sanity Testing là gì? Trong quá trình phát triển và vận hành một dự án phần mềm, hoạt động lập trình không bao giờ dừng lại ở lần phát hành đầu tiên. Sau khi nhận được phản hồi từ đội ngũ QC hoặc người dùng thực tế, các lập trình viên sẽ liên tục tiến […]

Laravel Routing & Controllers

Laravel Routing và Controllers là gì? 1. Laravel Routing là gì? Laravel Routing (định tuyến trong Laravel) là cơ chế xác định cách ứng dụng tiếp nhận và xử lý HTTP request dựa trên những thông tin như HTTP method và URL. Một route có thể chuyển request đến Controller, thực thi Closure (hàm ẩn […]

Functional Testing (Kiểm thử chức năng)

1.Functional Testing (Kiểm thử chức năng) là gì? Trong bức tranh toàn cảnh của ngành phát triển phần mềm, mục tiêu cuối cùng của mọi ứng dụng công nghệ là giải quyết các bài toán nghiệp vụ cho con người. Dù một hệ thống có kiến trúc mã nguồn siêu việt, giao diện bắt mắt […]

PHP & Laravel Fundamentals

PHP là gì? — Ngôn ngữ lập trình phía máy chủ và vai trò trong phát triển web PHP là gì?   PHP (viết tắt của PHP: Hypertext Preprocessor) là một ngôn ngữ lập trình phía máy chủ, thường được sử dụng để xây dựng website và ứng dụng web động. Trong mô hình phát […]

Leave a Reply

Your email address will not be published. Required fields are marked *