Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn vừa vẽ xong một sơ đồ quan hệ giữa hai entity Khách hàng và Đơn hàng. Bạn nối một đường thẳng giữa chúng và nghĩ rằng "thế là xong, hai bảng này liên quan với nhau". Nhưng khi đưa sơ đồ cho lập trình viên, họ lập tức hỏi lại: "Một khách hàng có thể có bao nhiêu đơn hàng? Có khách hàng nào không có đơn hàng nào không? Một đơn hàng có bắt buộc phải thuộc về một khách hàng không?".
Đây chính là khoảnh khắc bạn nhận ra: chỉ vẽ một đường thẳng nối hai entity là chưa đủ. Cái mà lập trình viên cần — và cái mà nghiệp vụ thực sự muốn diễn đạt — nằm ở cardinality notation, tức ký pháp diễn tả số lượng và tính bắt buộc của mối quan hệ.
Ở Bài 7, bạn đã học các loại quan hệ One-to-One, One-to-Many, Many-to-Many. Bài 7 trả lời câu hỏi "loại quan hệ là gì". Bài 8 này trả lời câu hỏi khác và sâu hơn: "làm thế nào để vẽ chính xác mức độ và tính bắt buộc của quan hệ đó trên sơ đồ ERD, theo những quy ước mà cả thế giới đọc được". Đây là kỹ năng phân biệt một Business Analyst nghiệp dư với một người chuyên nghiệp: cùng một mối quan hệ, người chuyên nghiệp diễn đạt được "tối thiểu bao nhiêu, tối đa bao nhiêu" chỉ bằng vài ký hiệu nhỏ ở đầu đường nối.
Nắm vững ba hệ ký pháp Crow's Foot, Chen và UML sẽ giúp bạn đọc được bất kỳ sơ đồ ERD nào trên thế giới, dù nó được vẽ bằng công cụ gì hay theo trường phái nào.
Khái niệm cốt lõi
Cardinality và Optionality — hai thành phần của một quan hệ
Trước khi đi vào ký pháp, bạn cần phân biệt rạch ròi hai khái niệm thường bị gộp lẫn:
- Cardinality (số lượng tối đa): một thực thể ở đầu này có thể liên kết với tối đa bao nhiêu thực thể ở đầu kia? Câu trả lời thường là "một" (1) hoặc "nhiều" (N/many).
- Optionality / Modality (tính bắt buộc, số lượng tối thiểu): mối liên kết đó có bắt buộc hay không? Tối thiểu là 0 (tùy chọn, optional) hay tối thiểu là 1 (bắt buộc, mandatory)?
(1,1)— chính xác một, không hơn không kém ("one and only one").(0,1)— không hoặc một ("zero or one").(1,N)— một hoặc nhiều ("one or many").(0,N)— không hoặc nhiều ("zero or many").
Hệ ký pháp 1 — Crow's Foot (Information Engineering)
Đây là hệ ký pháp phổ biến nhất tại Việt Nam và trên thế giới, được tích hợp mặc định trong hầu hết công cụ (Lucidchart, draw.io, dbdiagram, ERwin). Tên gọi "chân quạ" (crow's foot) bắt nguồn từ ký hiệu ba nhánh xòe ra như chân chim, biểu thị "nhiều".
Quy ước cơ bản đọc từ ngoài vào trong tại mỗi đầu đường nối:
- Vòng tròn
○= tối thiểu 0 (optional / tùy chọn). - Gạch ngang
|= tối thiểu hoặc tối đa là 1. - Chân quạ (ba nhánh)
<= nhiều (many).
|| ⏤ one and only one (đúng một, bắt buộc) → (1,1)
○| ⏤ zero or one (không hoặc một) → (0,1)
|< ⏤ one or many (một hoặc nhiều, bắt buộc) → (1,N)
○< ⏤ zero or many (không hoặc nhiều) → (0,N)
Cách đọc một đường nối hoàn chỉnh: bạn đọc ký hiệu ở phía đầu kia để biết một thực thể bên này liên kết với bao nhiêu thực thể bên kia. Ví dụ quan hệ Khách hàng — Đơn hàng:
KHÁCH HÀNG ||⏤⏤○< ĐƠN HÀNG
Đọc là: "Một khách hàng có không hoặc nhiều đơn hàng (đọc ký hiệu ○< sát Đơn hàng), và một đơn hàng thuộc về đúng một khách hàng (đọc ký hiệu || sát Khách hàng)". Đây là quan hệ One-to-Many điển hình, trong đó phía Đơn hàng là optional vì một khách hàng mới đăng ký có thể chưa mua gì.
Hệ ký pháp 2 — Chen Notation
Do Peter Chen đề xuất năm 1976, đây là hệ ký pháp "kinh điển" thường gặp trong giáo trình đại học và môi trường học thuật. Đặc trưng:
- Entity là hình chữ nhật.
- Relationship là hình thoi (rhombus) nằm giữa hai entity.
- Cardinality được ghi bằng chữ/số trên đường nối:
1,N,M.
[KHÁCH HÀNG] ⏤1⏤ <Đặt> ⏤N⏤ [ĐƠN HÀNG]
Phiên bản Chen mở rộng còn dùng cặp (min, max) đặt cạnh entity để diễn tả cả optionality, ví dụ (0,N). Chen rất mạnh để mô hình hóa quan hệ Many-to-Many và quan hệ có thuộc tính (attributes của relationship), nhưng tốn diện tích và ít dùng để triển khai database thực tế.
Hệ ký pháp 3 — UML (Unified Modeling Language)
UML dùng multiplicity (bội số) ghi ở đầu mỗi association line, theo dạng min..max:
1(viết tắt của1..1) — đúng một.0..1— không hoặc một.1..— một hoặc nhiều (dấunghĩa là "nhiều", không giới hạn).0..hoặc đơn giản— không hoặc nhiều.
KHÁCH HÀNG 1 ⏤⏤⏤⏤ 0..* ĐƠN HÀNG
Điểm cần lưu ý: trong UML, multiplicity được đặt ở đầu gần entity mà nó mô tả số lượng, ngược chiều trực giác so với cách đọc Crow's Foot một chút. Ở đây 0..* nằm sát Đơn hàng nghĩa là "một khách hàng tương ứng với 0 đến nhiều đơn hàng". UML phổ biến trong môi trường phát triển phần mềm hướng đối tượng và tài liệu thiết kế hệ thống.
Bảng đối chiếu nhanh ba hệ ký pháp
| Ý nghĩa | Crow's Foot | Chen | UML | |
|---|---|---|---|---|
| Đúng một | | 1 | 1 | |
| Không hoặc một | ○ | (0,1) | 0..1 | |
| Một hoặc nhiều | | < | (1,N) | 1..* |
| Không hoặc nhiều | ○< | (0,N) | 0.. hoặc |
Tình huống thực tế
Ví dụ 1 — Tiki và quan hệ Khách hàng – Đơn hàng – Sản phẩm
Giả sử bạn là BA tham gia thiết kế lại schema cho sàn thương mại điện tử Tiki. Đội kỹ thuật tranh luận về quan hệ giữa Khách hàng, Đơn hàng và Sản phẩm. Bạn vẽ bằng Crow's Foot:
KHÁCH HÀNG ||⏤⏤○< ĐƠN HÀNG ||⏤⏤|< CHI TIẾT ĐƠN >|⏤⏤|| SẢN PHẨM
Diễn giải:
Khách hàng ||⏤⏤○< Đơn hàng: một khách hàng có 0 đến nhiều đơn hàng (khách mới chưa mua gì vẫn hợp lệ); mỗi đơn hàng thuộc đúng một khách hàng.Đơn hàng ||⏤⏤|< Chi tiết đơn: một đơn hàng phải có ít nhất một dòng chi tiết (|<, tối thiểu 1) — vì không thể tồn tại đơn hàng rỗng.Sản phẩm ||⏤⏤>| Chi tiết đơn: một sản phẩm xuất hiện trong nhiều dòng chi tiết, mỗi dòng chi tiết tham chiếu đúng một sản phẩm.
Chi tiết đơn (associative entity) đã "tách" quan hệ Many-to-Many giữa Đơn hàng và Sản phẩm thành hai quan hệ One-to-Many. Và chính ký hiệu ||⏤⏤|< (bắt buộc tối thiểu 1) ở phía Đơn hàng – Chi tiết đã giúp đội kỹ thuật phát hiện cần thêm ràng buộc nghiệp vụ "không cho lưu đơn hàng trống".Ví dụ 2 — Ngân hàng VPBank và quan hệ tùy chọn dễ bị bỏ sót
Một BA tại dự án core banking của VPBank thiết kế quan hệ giữa Khách hàng và Tài khoản. Bản nháp đầu tiên vẽ:
KHÁCH HÀNG ||⏤⏤|< TÀI KHOẢN
Nghĩa là "mỗi khách hàng phải có ít nhất một tài khoản". Nhưng khi rà soát nghiệp vụ, đội phát hiện ngân hàng có khái niệm "khách hàng tiềm năng" — người đã được tạo hồ sơ KYC nhưng chưa mở tài khoản nào. Nếu giữ ký hiệu |< (tối thiểu 1), hệ thống sẽ từ chối lưu khách hàng tiềm năng, gây lỗi nghiệp vụ nghiêm trọng.
Họ sửa thành:
KHÁCH HÀNG ||⏤⏤○< TÀI KHOẢN
Chỉ một thay đổi nhỏ từ gạch | sang vòng tròn ○ ở phía Tài khoản — từ mandatory sang optional — đã phản ánh đúng thực tế. Trong UML, sự khác biệt này tương đương đổi 1.. thành 0...
Bài học rút ra: optionality (số tối thiểu) quan trọng không kém cardinality (số tối đa). Một ký hiệu vòng tròn nhỏ có thể là khác biệt giữa schema chạy được và schema chặn đứng cả một luồng nghiệp vụ. BA giỏi luôn hỏi: "Có trường hợp nào đầu này tồn tại mà đầu kia bằng 0 không?".
Ví dụ 3 — Grab và quan hệ One-to-One bị nhầm thành One-to-Many
Tại một dự án của Grab, đội thiết kế quan hệ giữa Tài xế và Hồ sơ xác minh (verification profile chứa CMND/CCCD, giấy phép lái xe). Một bạn mới vẽ theo Crow's Foot:
TÀI XẾ ||⏤⏤○< HỒ SƠ XÁC MINH
Tức "một tài xế có nhiều hồ sơ xác minh". Trong buổi review, một senior BA chỉ ra: theo nghiệp vụ, mỗi tài xế chỉ có đúng một hồ sơ xác minh đang hiệu lực, và mỗi hồ sơ thuộc về đúng một tài xế. Đây là One-to-One, không phải One-to-Many. Ký pháp đúng là:
TÀI XẾ ||⏤⏤○| HỒ SƠ XÁC MINH
Phía Hồ sơ dùng ○| (zero or one) vì tài xế mới đăng ký có thể chưa nộp hồ sơ. Trong UML viết là 1 ⏤ 0..1.
Bài học rút ra: nhầm chân quạ (<) với gạch đơn (|) làm sai hoàn toàn bản chất quan hệ, dẫn đến lập trình viên có thể tạo bảng cho phép nhiều hồ sơ trùng lặp — một lỗ hổng dữ liệu. Đọc kỹ từng ký hiệu, đừng vẽ theo quán tính.
Hướng dẫn từng bước
Để xác định và vẽ đúng cardinality cho một quan hệ, hãy làm theo quy trình bốn câu hỏi sau:
- Xác định hai entity và đặt câu hỏi hai chiều. Với mỗi cặp entity A – B, luôn hỏi cả hai chiều: "Một A liên kết với bao nhiêu B?" và "Một B liên kết với bao nhiêu A?". Đừng bao giờ chỉ hỏi một chiều.
- Tách riêng cardinality (tối đa) và optionality (tối thiểu). Với mỗi chiều, trả lời lần lượt: "Tối đa là 1 hay nhiều?" rồi "Tối thiểu là 0 hay 1?". Ghi lại dạng cặp
(min, max), ví dụ(0, N).
- Chuyển cặp
(min, max)sang ký hiệu của hệ bạn chọn. Dùng bảng đối chiếu phía trên. Nếu là Crow's Foot, nhớ quy tắc: ký hiệu phía trong (gần entity) là cardinality, ký hiệu phía ngoài là optionality. Đặt ký hiệu ở đầu đối diện với entity mà bạn đang mô tả.
- Kiểm tra chéo bằng cách đọc to thành câu nghiệp vụ. Đọc lại sơ đồ thành câu tiếng Việt rõ ràng: "Một khách hàng có thể có không hoặc nhiều đơn hàng; mỗi đơn hàng phải thuộc về đúng một khách hàng". Nếu câu này khớp với mô tả nghiệp vụ thực tế và được stakeholder gật đầu, bạn đã vẽ đúng.
Lỗi thường gặp & mẹo
- Quên optionality, chỉ vẽ cardinality. Rất nhiều người chỉ phân biệt "1 hay nhiều" mà bỏ qua "0 hay 1". Hậu quả: schema bắt buộc một quan hệ mà nghiệp vụ cho phép tùy chọn (như ví dụ VPBank). Luôn hỏi đủ cả tối thiểu lẫn tối đa.
- Đọc sai chiều ký hiệu Crow's Foot. Ký hiệu mô tả "một thực thể bên này có bao nhiêu thực thể bên kia" được đặt ở đầu gần entity bên kia. Người mới thường đọc ngược, dẫn đến hiểu sai hoàn toàn. Mẹo: luôn đọc to thành câu để tự kiểm tra.
- Nhầm lẫn giữa các hệ ký pháp trong cùng một sơ đồ. Đừng trộn
1..*(UML) với||⏤<(Crow's Foot) trong cùng một bản vẽ. Chọn một hệ và giữ nhất quán toàn bộ tài liệu; nếu không người đọc sẽ rối.
- Bỏ qua quan hệ Many-to-Many cần associative entity. Khi gặp
>⏤⏤<(nhiều–nhiều), đừng để nguyên; hãy nhớ rằng khi triển khai database, nó luôn cần một bảng trung gian. Cardinality notation giúp bạn phát hiện điều này sớm.
- Mẹo ghi nhớ Crow's Foot: "vòng tròn là số 0, gạch là số 1, chân quạ là nhiều". Cứ đọc từ ngoài vào trong: ký hiệu ngoài cùng cho tối thiểu, ký hiệu trong cùng cho tối đa.
- Mẹo với UML: dấu
luôn nghĩa là "nhiều/không giới hạn".0..1và1..là hai bội số bạn sẽ dùng nhiều nhất sau1và*.
Bài tập thực hành
Hãy áp dụng quy trình bốn câu hỏi cho các tình huống sau và vẽ bằng cả ba hệ ký pháp (Crow's Foot, Chen, UML):
- Trường đại học: quan hệ giữa
Sinh viênvàLớp học phần. Một sinh viên đăng ký nhiều lớp, một lớp có nhiều sinh viên. Sinh viên mới nhập học có thể chưa đăng ký lớp nào. Xác định cardinality, optionality, và cho biết quan hệ này cần associative entity gì khi triển khai.
- Bệnh viện: quan hệ giữa
Bệnh nhânvàBệnh án. Mỗi bệnh nhân có đúng một bệnh án chính; mỗi bệnh án thuộc đúng một bệnh nhân. Đây là loại quan hệ gì? Vẽ ký pháp cho cả ba hệ.
- Sàn TMĐT Shopee: quan hệ giữa
Người bán (Shop)vàSản phẩm. Một shop có thể chưa đăng sản phẩm nào (shop mới mở), nhưng mỗi sản phẩm phải thuộc về một shop. Vẽ ký pháp và đọc to thành câu nghiệp vụ để kiểm tra.
- Thử thách: cho sơ đồ Crow's Foot
A ○|⏤⏤|< B, hãy dịch ngược ra câu nghiệp vụ và chuyển sang ký pháp UML tương đương.
|< sát B); một B có không hoặc một A (○| sát A)". UML tương đương: A 0..1 ⏤⏤ 1..* B.Tóm tắt
- Cardinality notation diễn tả hai yếu tố của mỗi quan hệ: cardinality (số lượng tối đa: một hay nhiều) và optionality (số lượng tối thiểu: 0 hay 1). Bỏ sót optionality là lỗi phổ biến và nguy hiểm.
- Crow's Foot là hệ phổ biến nhất, phù hợp triển khai database: vòng tròn = 0, gạch = 1, chân quạ = nhiều. Đọc ký hiệu ở đầu đối diện entity.
- Chen dùng hình thoi cho relationship và chữ
1/N/Mtrên đường nối, mạnh trong học thuật và mô hình hóa Many-to-Many có thuộc tính. - UML dùng multiplicity dạng
min..max(1,0..1,1..,0..), phổ biến trong thiết kế phần mềm hướng đối tượng. - Luôn kiểm tra bằng cách đọc to thành câu nghiệp vụ hai chiều và xin xác nhận từ stakeholder. Một ký hiệu nhỏ (vòng tròn hay gạch) có thể quyết định schema chạy được hay chặn đứng luồng nghiệp vụ.