Vừa rồi mình được một người bạn nhờ làm bài tập về thiết kế cơ sở dữ liệu theo chuẩn 3NF, thú thực đây là những kiến thức học hồi đại học và cho đến giờ mình đã quên sạch )). Bởi vậy mình quyết định viết một bài để note lại những gì mình đã đọc và để làm tài liệu tham khảo sau này.
Mở đầu
Hầu hết các ứng dụng web đều cần phải có cơ sở dữ liệu để lưu trữ dữ liệu, xử lý thông tin, kết xuất đưa ra báo cáo thống kê, hỗ trợ tìm kiếm ... Khi mà dữ liệu trở thành linh hồn của ứng dụng web thì việc ứng dụng web hoạt động tốt trước hết là phải có một cơ sở dữ liệu lưu trữ hiệu quả. Mình đã từng làm những bài tập lớn mà chỉ cần thấy đề bài thôi là đã hăm hở lao vào code, và sau đó chỉ cần 1 chút thay đổi nhỏ cũng phải đập DB đi xây lại từ đầu. Từ đó mình rút ra 1 điều là, thiết kế cơ sở dữ liệu là 1 phần cực kì quan trọng, nếu thiết kế cẩn thận thì sau này bạn sẽ tiết kiệm được rất nhiều thời gian trong quá trình phát triển. Và để tối ưu cơ sở dữ liệu thì bạn nên tuân theo các chuẩn thiết kế mà mình nêu dưới đây nhé.
Mục đích của chuẩn hóa cơ sở dữ liệu
Như mình đã nói ở phần mở đầu, chuẩn hóa cơ sở dữ liệu nhằm 2 mục đích:
1. Giảm thiểu dư thừa dữ liệu
2. Loại bỏ các bất thường khi cập nhật cơ sở dữ liệu
Các kiểu chuẩn hóa cơ sở dữ liệu
Có 4 dạng chuẩn hóa cơ bản đó là:
-
First Normal Form (1NF): dạng chuẩn 1NF
-
Second Normal Form (2NF): dạng chuẩn 2NF
-
Third Nomal Form (3NF): dạng chuẩn 3NF
-
Boyce-Codd Normal Form (BCNF): dạng chuẩn Boyce-Codd
Các dạng chuẩn được sắp xếp theo thứ tự từ thấp đến cao. Để chuẩn hóa 2NF thì cơ sở dữ liệu của bạn phải đạt chuẩn 1NF, tương tự nếu đạt chuẩn 3NF thì phải đạt chuẩn 1NF và 2NF. Và chuẩn Boyce-Codd sẽ bao gồm 3 loại chuẩn 1NF, 2NF và 3NF.
Dạng chuẩn 1NF
Ví dụ 1 bảng chưa chuẩn hóa:
Bảng có 3 khóa chính là customer_id
, order_id
và product_id
.
Bảng dữ liệu này vi phạm cả điều kiện của chuẩn 1NF vì: address
chứa các giá trị trùng lặp, hơn thế nữa, giá trị address
trong từng hàng không phải là đơn trị (chỉ có 1 giá trị), thêm vào đó, thuộc tính total_amount
hoàn toàn có thể tính toán được bằng cách quantity * unit_price
, không nhất thiết phải đưa vào bảng, gây ra dư thừa dữ liệu.
Qua nhận xét trên, ta có thể hình dung ra 3 điều kiện cần phải tuân theo đó là:
-
Các thuộc tính của bảng phải là nguyên tố
-
Giá trị của các thuộc tính trên các hàng phải là đơn trị, không chứa nhóm lặp
-
Không có một thuộc tính nào có giá trị có thể tính toán được từ một thuộc tính khác
Từ đó, ta có thể thiết kế lại bảng dữ liệu trên như sau:
- Tách các thuộc tính lặp trong bảng như:
customer_name
,phone
ra thành một bảng mới làcustomers
- Tách
address
thành một bảng riêng có khóa làcustomer_id
để biết địa chỉ đó thuộc vềcustomer
nào. - Loại bỏ thuộc tính
total_amount
Kết quả như sau:
Dạng chuẩn 2NF
Quy tắc chuẩn hóa từ chuẩn 1NF thành 2NF:
-
Bước 1: Loại bỏ các thuộc tính không khóa phụ thuộc vào một bộ phận khóa chính và tách ra thành một bảng riêng, khóa chính của bảng là bộ phận của khóa mà chúng phụ thuộc vào.
-
Bước 2: Các thuộc tính còn lại lập thành một quan hệ, khóa chính của nó là khóa chính ban đầu.
Bảng dữ liệu mới mà ta thiết kế vẫn chưa đạt chuẩn 2NF là vì: một số thuộc tính như description
, unit_price
phụ thuộc vào 1 phần của khóa là product_id
chứ không cần phụ thuộc cả vào tập khóa (customer_id
, order_id
, product_id
), hay thuộc tính customer_name
và phone
cũng chỉ phụ thuộc vào customer_id
, thuộc tính order_date
phụ thuộc vào customer_id
và order_id
, thuộc tính quantity
phụ thuộc vào order_id
và product_id
.
Vậy nên để đạt chuẩn 2NF thì ta sẽ thiết kế tiếp bảng dữ liệu chuẩn 1NF như sau:
- Tách các thuộc tính (
product_id
,description
,unit_price
) thành một bảng riêng làproducts
. - Các thuộc tính (
customer_id
,order_id
,order_date
) làm thành một bảng, mình đặt tên làorders
. - Còn lại các thuộc tính (
order_id
,product_id
,quantity
) làm thành một bảng trung gian giữaproducts
vàorders
, mình đặt làorder_products
.
Chỉ cần tuân thủ 2 chuẩn mà ta đã được cơ sở dữ liệu chuẩn hóa như sau:
Dạng chuẩn 3NF
Điều kiện:
-
Phải đạt chuẩn 2NF
-
Mọi thuộc tính không khóa phụ thuộc bắc cầu vào thuộc tính khóa (nghĩa là tất cả các thuộc tính không khóa phải được suy ra trực tiếp từ thuộc tính khóa)
Quy tắc chuẩn hóa từ 2NF thành 3NF:
-
Bước 1: Loại bỏ các thuộc tính phụ thuộc bắc cầu ra khỏi quan hệ và tách chúng thành quan hệ riêng có khóa chính là thuộc tính bắc cầu.
-
Bước 2: Các thuộc tính còn lại lập thành một quan hệ có khóa chính là khóa ban đầu.
Để ý thấy cơ sở dữ liệu mà ta thiết kế ở chuẩn 2NF cũng đã đạt chuẩn 3NF. Thế nên mình sẽ lấy một ví dụ khác để các bạn tham khảo như sau:
Ví dụ bảng sau vi phạm chuẩn 3NF:
Ta thấy thuộc tính country_name
phụ thuộc vào country_id
, mà country_id
lại phụ thuộc vào khóa chính là id
. Vì vậy ta nên tách bảng trên thành 2 bảng sau:
Dạng chuẩn Boyce-Codd
Điều kiện:
-
Phải đạt chuẩn 3NF
-
Không có thuộc tính khóa nào phụ thuộc vào thuộc tính không khóa
Quy tắc chuẩn hóa 3NF thành Boyce-Codd:
-
Bước 1: Loại bỏ các thuộc tính khóa phụ thuộc hàm vào thuộc tính không khóa ra khỏi quan hệ
-
Bước 2: Tách thuộc tính vừa loại bỏ thành một quan hệ riêng có khoá chính là thuộc tính không khóa gây ra phụ thuộc.
Mình chưa nghĩ ra ví dụ thực tế nên mình xin mô tả sau: Giả sử bảng có các thuộc tính (A, B, C, D, E, F) với AB là khóa chính AB => C AB => D AB => E F => B Ta thấy bảng trên vi phạm chuẩn Boyce-Codd vì B là thuộc tính khóa nhưng lại phụ thuộc vào F là thuộc tính không khóa. Từ đó, ta sẽ tách bảng ra để theo chuẩn như sau: Bảng 1: (A, F, C, D, E) Bảng 2: (F, B) Mình thấy thường chỉ cần tuân theo 3 chuẩn 1NF -> 3NF là cơ sở dữ liệu của mình đã được tối ưu rồi . Chuẩn này mình nêu ra với mục đích tham khảo thêm và còn 1 số chuẩn cao hơn nữa mình chưa nói đến. Có thể sau này các bạn phải thiết kế dữ liệu lớn thì có thể mắc phải những lỗi mình nêu trên. Và nếu các bạn tìm được một ví dụ về bảng chưa đạt chuẩn Boyce-Codd thì comment cho mình tham khảo với nhé.
Bài viết của mình đến đây là kết thúc, mặc dù còn nhiều thiếu sót nhưng hi vọng sẽ đem đến 1 ít kiến thức cho các bạn mới bắt tay vào code như mình. Thank you !