Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Bài 12 — Normalization — 1NF Cơ Bản

Data Modeling and ERD Mastery Bài 12/60

Mở đầu — vì sao bài này quan trọng

Hãy tưởng tượng bạn vừa nhận một file Excel quản lý đơn hàng từ phòng kinh doanh của một chuỗi mỹ phẩm. Trong một ô (cell) duy nhất ở cột "Sản phẩm", bạn thấy: "Son 3CE, Kem chống nắng Anessa, Mặt nạ Mediheal". Một ô khác lại chỉ có "Nước hoa hồng". Còn cột "Số điện thoại khách" thì có ô ghi "0901234567 / 0912345678" vì khách có hai số.

Trông thì vô hại, thậm chí còn "tiện" cho người nhập liệu. Nhưng khi bạn — với vai trò Business Analyst (BA) — phải trả lời những câu hỏi tưởng chừng đơn giản như "Sản phẩm nào bán chạy nhất tháng này?" hoặc "Khách hàng nào mua kem chống nắng Anessa?", bạn sẽ vỡ mộng. Không có cách nào viết một câu truy vấn sạch sẽ, không có cách nào tính tổng chính xác, và mỗi lần báo cáo bạn lại phải tách chuỗi thủ công.

Đây chính là lý do First Normal Form (1NF) ra đời. Nó là nấc thang đầu tiên trong chuỗi chuẩn hóa (normalization) và cũng là nền tảng bắt buộc trước khi bạn nghĩ tới 2NF hay 3NF (sẽ học ở Bài 13 và Bài 14). Nếu một bảng còn chưa đạt 1NF thì mọi nỗ lực chuẩn hóa phía sau đều vô nghĩa.

Trong bài này, chúng ta tập trung duy nhất vào 1NF: hiểu thế nào là atomic value (giá trị nguyên tử), thế nào là repeating group (nhóm lặp), nhận diện vi phạm, và biến một bảng "bẩn" thành bảng đạt chuẩn 1NF. Đây là kỹ năng nền mà bất kỳ BA làm việc với dữ liệu nào cũng phải thành thạo.

Khái niệm cốt lõi

Định nghĩa 1NF

Một bảng (table/relation) đạt First Normal Form (1NF) khi thỏa mãn đồng thời ba điều kiện:

  • Mỗi cell chỉ chứa một giá trị nguyên tử (atomic value) — tức là giá trị không thể (và không nên) chia nhỏ hơn về mặt nghiệp vụ. Một ô không được chứa danh sách như "Son, Kem, Mặt nạ".
  • Không có nhóm lặp (repeating group) — tức là không có chuyện tạo ra các cột kiểu SanPham1, SanPham2, SanPham3 để nhồi nhiều giá trị cùng loại vào một dòng.
  • Mỗi dòng phải được phân biệt duy nhất (thường là nhờ một primary key) và thứ tự dòng không mang ý nghĩa.
Hai điều kiện đầu là phần "linh hồn" của 1NF mà bạn cần khắc cốt ghi tâm. Điều kiện thứ ba thực ra là yêu cầu nền tảng của mô hình quan hệ (relational model) nói chung.

Atomic value — "nguyên tử" nghĩa là gì?

"Atomic" mượn từ vật lý: nguyên tử là đơn vị nhỏ nhất không chia nhỏ thêm được nữa (trong bối cảnh nghiệp vụ). Nhưng "atomic" không phải khái niệm tuyệt đối — nó phụ thuộc vào nhu cầu nghiệp vụ của bạn.

Ví dụ cột HoTen chứa "Nguyễn Thị Lan Anh":

  • Nếu hệ thống của bạn không bao giờ cần tách họ và tên riêng, thì coi "Nguyễn Thị Lan Anh" là một giá trị atomic là chấp nhận được.
  • Nhưng nếu bạn cần lọc theo họ, sắp xếp theo tên, hay gọi khách bằng tên riêng trong email marketing ("Chào Lan Anh"), thì "Nguyễn Thị Lan Anh" không còn atomic với nhu cầu đó, và bạn nên tách thành các cột riêng (chủ đề này sẽ được đào sâu ở Bài 26 về Vietnamese Names).
Điểm mấu chốt: atomic được xác định bởi mức độ chi tiết nhỏ nhất mà nghiệp vụ cần truy vấn và xử lý độc lập. Một số tín hiệu cho thấy một cell đang vi phạm tính atomic:

  • Trong cell có dấu phân tách: dấu phẩy, gạch chéo, xuống dòng, chấm phẩy ("son, kem, mặt nạ").
  • Bạn thấy mình phải dùng hàm tách chuỗi (SPLIT, LIKE '%...%') để lấy thông tin ra.
  • Cùng một loại thông tin lại được "gộp" vào một ô.

Repeating group — nhóm lặp

Repeating group là khi cùng một loại thuộc tính bị lặp lại thành nhiều cột trong một dòng. Đây là "anh em sinh đôi" của vi phạm atomic, chỉ khác là người ta cố nhồi nhiều giá trị theo chiều ngang (cột) thay vì gộp vào một ô.

Ví dụ bảng DonHang có các cột: MaDon, SanPham1, SanPham2, SanPham3. Đây là repeating group kinh điển. Hậu quả:

  • Nếu khách mua 4 sản phẩm thì sao? Bạn lại phải thêm cột SanPham4, sửa cả cấu trúc bảng — một cơn ác mộng bảo trì.
  • Hầu hết các dòng sẽ có SanPham2, SanPham3 để trống (NULL), gây lãng phí và khó truy vấn.
  • Câu hỏi "sản phẩm X xuất hiện trong bao nhiêu đơn?" phải quét qua cả ba cột.

Vì sao 1NF quan trọng đến vậy

Một bảng đạt 1NF mở khóa cho bạn:

  • Truy vấn sạch: đếm, lọc, nhóm (GROUP BY) dữ liệu dễ dàng.
  • Toàn vẹn dữ liệu: ràng buộc (constraint) áp được lên từng giá trị riêng lẻ.
  • Khả năng mở rộng: thêm sản phẩm thứ N không cần đổi cấu trúc bảng.
  • Nền tảng cho 2NF, 3NF: bạn không thể leo lên nếu chưa đứng vững ở bậc một.

Tình huống thực tế

Ví dụ 1 — Tiệm mỹ phẩm bán hàng đa kênh (vi phạm atomic value)

Bối cảnh: Một cửa hàng mỹ phẩm online ở TP.HCM dùng Google Sheets để theo dõi đơn hàng. Bảng DonHang của họ trông như sau:

MaDonKhachHangSanPhamSoDienThoai
1001Trần MaiSon 3CE, Kem Anessa, Mặt nạ Mediheal0901234567 / 0912345678
1002Lê HùngNước hoa hồng0987654321
Diễn giải: Cột SanPham nhồi nhiều sản phẩm vào một ô — vi phạm atomic. Cột SoDienThoai của khách Trần Mai chứa hai số ngăn cách bằng dấu "/". Khi quản lý hỏi "Tháng này bán được bao nhiêu hộp kem Anessa?", nhân viên phải mở từng dòng, đọc bằng mắt và đếm tay. Mỗi lần khuyến mãi, con số báo cáo lại sai lệch vì không ai tách chuỗi chính xác.

Cách sửa: Tách mỗi sản phẩm thành một dòng riêng. Tách số điện thoại thành các bản ghi riêng (hoặc một bảng SoDienThoai liên kết với khách). Sau khi tách, bảng chi tiết đơn hàng trở thành:

MaDonKhachHangSanPham
1001Trần MaiSon 3CE
1001Trần MaiKem Anessa
1001Trần MaiMặt nạ Mediheal
1002Lê HùngNước hoa hồng
Bài học rút ra: Khi một ô chứa danh sách, hãy "đập" danh sách đó ra theo chiều dọc (nhiều dòng), không phải theo chiều ngang (nhiều cột). Câu hỏi "bán bao nhiêu Anessa" giờ chỉ còn là một dòng COUNT đơn giản.

Ví dụ 2 — Phần mềm tuyển sinh trung tâm tiếng Anh (vi phạm repeating group)

Bối cảnh: Một trung tâm Anh ngữ ở Đà Nẵng thiết kế bảng HocVien để lưu các khóa học mà mỗi học viên đăng ký:

MaHVTenHVKhoaHoc1KhoaHoc2KhoaHoc3
HV01Phạm AnhIELTS 6.5Giao tiếpNULL
HV02Vũ LinhTOEIC 750NULLNULL
HV03Đỗ NamIELTS 6.5Giao tiếpPhát âm
Diễn giải: Ba cột KhoaHoc1, KhoaHoc2, KhoaHoc3 là repeating group điển hình. Vấn đề bộc lộ ngay khi:
  • Một học viên muốn đăng ký khóa thứ 4 — không có chỗ chứa, phải sửa cấu trúc bảng.
  • Phòng đào tạo hỏi "Bao nhiêu học viên đang học khóa IELTS 6.5?" — phải kiểm tra cả ba cột, viết điều kiện WHERE KhoaHoc1='IELTS 6.5' OR KhoaHoc2='IELTS 6.5' OR KhoaHoc3='IELTS 6.5', rất dễ sót.
  • Đa số ô bị NULL, gây lãng phí và khó thống kê.
Cách sửa: Tách quan hệ "học viên đăng ký khóa học" thành một bảng riêng DangKy:

MaHVKhoaHoc
HV01IELTS 6.5
HV01Giao tiếp
HV02TOEIC 750
HV03IELTS 6.5
HV03Giao tiếp
HV03Phát âm
Bài học rút ra: Bất cứ khi nào bạn thấy các cột đánh số _1, _2, _3, đó là tín hiệu đỏ của repeating group. Hãy chuyển chúng thành các dòng trong một bảng quan hệ con. Học viên đăng ký bao nhiêu khóa cũng được, cấu trúc không đổi.

Ví dụ 3 — Hệ thống giao hàng (atomic phụ thuộc nghiệp vụ)

Bối cảnh: Một công ty giao hàng nội thành lưu địa chỉ người nhận trong một cột duy nhất:

MaDonDiaChi
2001123 Lê Lợi, Phường Bến Thành, Quận 1, TP.HCM
Diễn giải: Câu hỏi đặt ra: cột DiaChi này có vi phạm 1NF không? Câu trả lời là tùy nghiệp vụ.
  • Nếu hệ thống chỉ in nhãn giao hàng và không bao giờ cần lọc theo quận/phường, thì coi cả chuỗi là atomic cũng tạm ổn.
  • Nhưng công ty này cần phân tuyến giao hàng theo Quậntính phí ship theo Phường. Lúc này, "Quận 1" và "Phường Bến Thành" là những đơn vị nghiệp vụ cần truy vấn độc lập, nên chuỗi địa chỉ gộp đã vi phạm tính atomic đối với nhu cầu của họ.
Cách sửa: Tách thành các cột SoNhaDuong, Phuong, Quan, TinhThanh (chủ đề mô hình hóa địa chỉ Việt Nam sẽ được đào sâu ở Bài 25):

MaDonSoNhaDuongPhuongQuanTinhThanh
2001123 Lê LợiBến ThànhQuận 1TP.HCM
Bài học rút ra: Atomic không phải hằng số tuyệt đối — nó được quyết định bởi mức chi tiết mà nghiệp vụ cần xử lý độc lập. Là BA, bạn phải hỏi: "Người dùng có bao giờ cần lọc/nhóm/tính toán dựa trên một phần của giá trị này không?" Nếu có, hãy tách.

Hướng dẫn từng bước

Đây là quy trình thực dụng để đưa một bảng bất kỳ về 1NF:

Bước 1 — Liệt kê toàn bộ cột và quan sát dữ liệu mẫu. Đừng chỉ nhìn tên cột; hãy nhìn dữ liệu thật. Nhiều vi phạm 1NF chỉ lộ ra khi bạn thấy giá trị thực tế (ví dụ một ô có dấu phẩy giữa các giá trị).

Bước 2 — Soi từng cột tìm vi phạm atomic. Đặt câu hỏi cho mỗi cột: "Một ô ở đây có khả năng chứa nhiều giá trị không? Có dấu phân tách (phẩy, gạch chéo, xuống dòng) không? Nghiệp vụ có cần truy vấn từng phần riêng không?" Đánh dấu những cột nghi ngờ.

Bước 3 — Soi cấu trúc bảng tìm repeating group. Tìm các cột được đánh số hoặc lặp ý nghĩa: SanPham1/2/3, DienThoai_Nha/DienThoai_DiDong (nếu cùng loại "số điện thoại"), v.v.

Bước 4 — Quyết định mức atomic phù hợp với nghiệp vụ. Với mỗi cột nghi ngờ, xác định: nghiệp vụ cần chi tiết đến đâu? Họ tên có cần tách không? Địa chỉ có cần tách quận/phường không? Đây là bước cần "đội mũ BA" thay vì chỉ máy móc tách mọi thứ.

Bước 5 — Áp dụng phép biến đổi.

  • Vi phạm atomic (nhiều giá trị trong một ô, danh sách): tách thành nhiều dòng, mỗi giá trị một dòng.
  • Vi phạm atomic (giá trị tổng hợp như họ tên, địa chỉ): tách thành nhiều cột, mỗi thành phần một cột.
  • Repeating group (cột đánh số): chuyển sang một bảng con với quan hệ một-nhiều.
Bước 6 — Xác định primary key cho bảng kết quả. Mỗi bảng phải có khóa chính phân biệt từng dòng (chủ đề Primary Key được học kỹ ở Bài 6). Ví dụ bảng chi tiết đơn hàng có thể dùng khóa kết hợp (MaDon, SanPham).

Bước 7 — Kiểm tra lại. Quét toàn bộ bảng mới: mỗi ô đúng một giá trị atomic chưa? Không còn cột đánh số chứ? Mỗi dòng phân biệt duy nhất chưa? Nếu cả ba đều "có" thì bạn đã đạt 1NF.

Lỗi thường gặp & mẹo

Lỗi 1 — Nhầm "tách thành cột" với "tách thành dòng". Khi gặp một ô chứa danh sách nhiều giá trị cùng loại (nhiều sản phẩm, nhiều khóa học), bạn phải tách thành dòng, không phải cột — vì tách thành cột lại tạo ra repeating group. Ngược lại, khi gặp một giá trị tổng hợp nhiều thành phần khác loại (họ + tên, số nhà + phường + quận), bạn tách thành cột.

Lỗi 2 — Tách quá đà. Không phải mọi thứ đều cần tách đến nguyên tử nhỏ nhất. Nếu nghiệp vụ không bao giờ cần lọc theo họ riêng, việc tách HoTen thành ba cột chỉ làm phức tạp thêm. Hãy để nhu cầu nghiệp vụ dẫn dắt, đừng chuẩn hóa vì "cho đẹp".

Lỗi 3 — Coi cột JSON/mảng là vi phạm 1NF một cách máy móc. Các CSDL hiện đại hỗ trợ kiểu JSON, array. Về lý thuyết quan hệ thuần túy, một cột chứa mảng vi phạm 1NF. Nhưng trong thực tế hiện đại, đôi khi lưu JSON là quyết định thiết kế có chủ đích (sẽ bàn ở Bài 29). Ở giai đoạn học nền tảng này, hãy nắm vững nguyên tắc 1NF cổ điển trước; biết khi nào "phá luật có chủ đích" là chuyện về sau.

Lỗi 4 — Quên xử lý dữ liệu lịch sử khi đã lỡ vi phạm. Sửa cấu trúc bảng là một chuyện; nhưng dữ liệu cũ "Son, Kem, Mặt nạ" vẫn nằm đó. Bạn cần một bước migration để tách dữ liệu cũ (chủ đề Migration được học ở Bài 30).

Mẹo: Một "đèn báo" cực nhanh để nghi ngờ vi phạm 1NF — nếu bạn thấy mình sắp viết LIKE '%,%', dùng hàm SPLIT/SUBSTRING_INDEX để tách chuỗi, hoặc viết OR lặp đi lặp lại trên các cột đánh số, thì gần như chắc chắn bảng đang chưa đạt 1NF.

Mẹo: Khi phỏng vấn người dùng nghiệp vụ, hãy hỏi thẳng: "Anh/chị có bao giờ cần đếm, lọc, hay tính tổng dựa trên một phần của thông tin này không?" Câu trả lời sẽ cho bạn biết mức atomic cần thiết.

Bài tập thực hành

Cho bảng DonDatBan của một nhà hàng lẩu ở Hà Nội dưới đây. Hãy phân tích và chuẩn hóa về 1NF.

MaBanTenKhachMonAnGioPhucVu
B01Nguyễn HàLẩu thái, Rau muống, Nấm18:30
B02Trần BìnhLẩu nấm, Bò Mỹ19:00
B03Lê CườngLẩu thái20:15
Yêu cầu:

  • Chỉ ra (các) cột đang vi phạm 1NF và giải thích vi phạm thuộc loại nào (atomic hay repeating group).
  • Vẽ lại bảng đã chuẩn hóa về 1NF.
  • Đề xuất primary key cho bảng kết quả.
  • Câu hỏi mở rộng: Nếu nghiệp vụ cần thống kê "món nào được gọi nhiều nhất trong tuần", giải thích vì sao bảng gốc không thể trả lời còn bảng đã chuẩn hóa thì có thể.
Gợi ý đáp án (hãy tự làm trước khi xem):

  • Cột MonAn vi phạm atomic — mỗi ô chứa danh sách nhiều món ngăn cách bằng dấu phẩy.
  • Tách mỗi món thành một dòng riêng:
MaBanTenKhachMonAnGioPhucVu
B01Nguyễn HàLẩu thái18:30
B01Nguyễn HàRau muống18:30
B01Nguyễn HàNấm18:30
B02Trần BìnhLẩu nấm19:00
B02Trần BìnhBò Mỹ19:00
B03Lê CườngLẩu thái20:15
  • Primary key: khóa kết hợp (MaBan, MonAn) — phân biệt được từng dòng (giả định mỗi bàn không gọi trùng món; nếu có thể trùng, cần thêm một mã dòng riêng).
  • Bảng gốc không thể đếm món vì các món bị "khóa" trong chuỗi văn bản — muốn đếm phải tách chuỗi thủ công và dễ sai. Bảng đã chuẩn hóa cho phép GROUP BY MonAn rồi COUNT, trả lời câu hỏi chỉ trong một truy vấn.

Tóm tắt

First Normal Form (1NF) là bậc đầu tiên và bắt buộc của chuẩn hóa. Một bảng đạt 1NF khi: mỗi ô chứa đúng một giá trị atomic (không chia nhỏ hơn theo nhu cầu nghiệp vụ), không có repeating group (không có cột đánh số _1, _2, _3), và mỗi dòng phân biệt duy nhất.

Những điều cần khắc cốt ghi tâm:

  • Danh sách nhiều giá trị cùng loại trong một ô → tách thành nhiều dòng.
  • Giá trị tổng hợp nhiều thành phần khác loại → tách thành nhiều cột (nếu nghiệp vụ cần).
  • Cột đánh số lặp lại → chuyển sang bảng con với quan hệ một-nhiều.
  • Atomic phụ thuộc nghiệp vụ: hãy để nhu cầu truy vấn của người dùng quyết định mức chi tiết, đừng tách quá đà cũng đừng gộp ẩu.
Khi bảng của bạn đã vững ở 1NF, bạn đã sẵn sàng leo lên bậc tiếp theo: xử lý partial dependencies trong 2NF (Bài 13). Hãy nhớ — không có nền móng 1NF chắc chắn, mọi tầng chuẩn hóa phía trên đều xây trên cát.