Database mẫu truong_hoc¶
Toàn bộ 51 bài học đều dùng chung một cơ sở dữ liệu duy nhất: truong_hoc, mô phỏng hệ thống quản lý của một trường THCS.
Vì sao chỉ dùng một database cho cả khóa học?
Vì mỗi lần đổi bối cảnh là một lần bạn phải học lại "ai là ai, bảng nào chứa gì". Dùng cố định một bộ dữ liệu, bạn chỉ tốn công làm quen một lần ở đây, rồi dành toàn bộ sức chú ý cho khái niệm mới của từng bài.
Phần thưởng đến ở Cấp 4: chính cái database bạn đã thuộc lòng sẽ được đem ra mổ xẻ — đánh index cho nó, đọc EXPLAIN của nó, rồi ở Cấp 5 là chia nhỏ nó ra nhiều máy.
Ba file dữ liệu¶
| File | Dùng cho | Nội dung |
|---|---|---|
dataset/01-chua-chuan-hoa.sql |
Cấp 2 (Bài 16–20) | Một bảng bẹt duy nhất, cố ý thiết kế tồi. Đây là "bệnh nhân" để bạn thực hành chuẩn hoá. |
dataset/02-chuan-hoa.sql |
Bài 5 trở đi | Lược đồ 10 bảng đã chuẩn hoá + dữ liệu mẫu. Đây là bản chính. |
dataset/03-du-lieu-lon.sql |
Cấp 4–5 (Bài 33+) | Sinh 500.000 dòng điểm. Cần dữ liệu lớn thì index và EXPLAIN mới có ý nghĩa. |
Tải về tại thư mục dataset/ trên GitHub. Hướng dẫn cài đặt và nạp nằm ở Bài 5.
Sơ đồ quan hệ thực thể¶
Đây là biểu đồ ER (ER diagram) của toàn bộ database, vẽ theo ký hiệu Crow's Foot. Bây giờ bạn chưa cần hiểu các ký hiệu ở hai đầu đường nối — Bài 11 sẽ dạy kỹ. Cứ quay lại trang này khi cần tra cứu.
erDiagram
giao_vien ||--o{ phan_cong_day : "được phân công"
giao_vien |o--o| lop : "chủ nhiệm"
lop ||--o{ hoc_sinh : "gồm có"
lop ||--o{ phan_cong_day : "được dạy tại"
hoc_sinh ||--o{ phu_huynh : "có"
hoc_sinh ||--o{ diem : "đạt"
hoc_sinh ||--o{ muon_sach : "mượn"
hoc_sinh ||--o{ diem_danh : "được điểm danh"
mon_hoc ||--o{ diem : "thuộc về"
mon_hoc ||--o{ phan_cong_day : "của môn"
sach ||--o{ muon_sach : "được mượn"
giao_vien {
char ma_gv PK
varchar ho_ten
date ngay_sinh
varchar gioi_tinh
varchar mon_chuyen_mon
varchar email UK
numeric luong
}
lop {
char ma_lop PK
varchar ten_lop UK
smallint khoi
varchar nam_hoc
char ma_gvcn FK
}
hoc_sinh {
char ma_hs PK
varchar ho_ten
date ngay_sinh
varchar gioi_tinh
varchar dia_chi
char ma_lop FK
}
phu_huynh {
char ma_ph PK
varchar ho_ten
varchar so_dien_thoai
varchar quan_he
char ma_hs FK
}
mon_hoc {
char ma_mon PK
varchar ten_mon UK
smallint so_tiet_tuan
}
phan_cong_day {
char ma_gv PK, FK
char ma_mon PK, FK
char ma_lop PK, FK
smallint hoc_ky PK
}
diem {
serial ma_diem PK
char ma_hs FK
char ma_mon FK
smallint hoc_ky
varchar loai_diem
numeric diem_so
date ngay_nhap
}
sach {
char ma_sach PK
varchar ten_sach
varchar tac_gia
smallint nam_xuat_ban
smallint so_luong
}
muon_sach {
serial ma_muon PK
char ma_hs FK
char ma_sach FK
date ngay_muon
date ngay_tra_du_kien
date ngay_tra_thuc_te
}
diem_danh {
serial ma_dd PK
char ma_hs FK
date ngay
varchar trang_thai
varchar ly_do
}
Mười bảng làm gì?¶
| Bảng | Giải thích đời thường | Số dòng mẫu |
|---|---|---|
giao_vien |
Danh sách thầy cô trong trường | 8 |
lop |
Các lớp học, kèm giáo viên chủ nhiệm | 6 |
hoc_sinh |
Danh sách học sinh, mỗi bạn thuộc một lớp | 40 |
phu_huynh |
Bố mẹ / ông bà của học sinh, kèm số điện thoại | 45 |
mon_hoc |
Các môn được dạy và số tiết mỗi tuần | 9 |
phan_cong_day |
Thầy nào dạy môn nào cho lớp nào, học kỳ mấy | 64 |
diem |
Từng con điểm của từng học sinh ở từng môn | 480 |
sach |
Kho sách thư viện | 20 |
muon_sach |
Từng lượt mượn — ai mượn cuốn nào, đã trả chưa | 50 |
diem_danh |
Mỗi ngày mỗi học sinh có mặt hay vắng | 200 |
Ba chi tiết được cài sẵn có chủ đích¶
Dữ liệu mẫu không phải sinh ngẫu nhiên hoàn toàn. Ba tình huống sau được cố ý đặt vào, vì các bài học cần chúng:
1. Lớp 9A3 chưa có giáo viên chủ nhiệm
Cột lop.ma_gvcn của lớp này là NULL. Nhờ vậy ở Bài 25 bạn sẽ thấy rõ sự khác nhau giữa INNER JOIN (lớp này biến mất) và LEFT JOIN (lớp này vẫn hiện, phần giáo viên để trống).
2. Học sinh HS040 — Đinh Thị Vân — không có phụ huynh nào trong hệ thống
Dùng để dạy NOT EXISTS và LEFT JOIN ... IS NULL ở Bài 27, và để minh hoạ vì sao NOT IN gặp NULL lại cho kết quả bất ngờ.
3. Một phần tư số lượt mượn sách có ngay_tra_thuc_te là NULL
NULL ở đây mang nghĩa "chưa trả". Đây là nguyên liệu để Bài 24 giải thích logic ba giá trị — cái bẫy mà gần như người mới nào cũng sập.
Bảng chi tiết các cột¶
Bảng dưới đây là nguồn tra cứu chính thức. Mọi câu SQL trong khóa học đều dùng đúng các tên này.
| Cột | Kiểu | Ràng buộc | Ý nghĩa |
|---|---|---|---|
ma_gv |
CHAR(4) |
PK | Mã giáo viên, dạng GV01 |
ho_ten |
VARCHAR(60) |
NOT NULL |
Họ và tên đầy đủ |
ngay_sinh |
DATE |
NOT NULL |
Ngày sinh |
gioi_tinh |
VARCHAR(3) |
CHECK IN ('Nam','Nữ') |
Giới tính |
mon_chuyen_mon |
VARCHAR(30) |
NOT NULL |
Môn được đào tạo để dạy |
email |
VARCHAR(80) |
UNIQUE | Khoá dự tuyển — xem Bài 12 |
luong |
NUMERIC(12,2) |
CHECK > 0 |
Lương tháng, đơn vị đồng |
| Cột | Kiểu | Ràng buộc | Ý nghĩa |
|---|---|---|---|
ma_lop |
CHAR(3) |
PK | Mã lớp, dạng L01 |
ten_lop |
VARCHAR(10) |
UNIQUE | Tên hiển thị, ví dụ 8A1 |
khoi |
SMALLINT |
CHECK 6..9 |
Khối lớp |
nam_hoc |
VARCHAR(9) |
NOT NULL |
Ví dụ 2025-2026 |
ma_gvcn |
CHAR(4) |
FK → giao_vien, UNIQUE, cho phép NULL |
Giáo viên chủ nhiệm. UNIQUE là cách hiện thực quan hệ 1:1 — xem Bài 14 |
| Cột | Kiểu | Ràng buộc | Ý nghĩa |
|---|---|---|---|
ma_hs |
CHAR(5) |
PK | Mã học sinh, dạng HS001 |
ho_ten |
VARCHAR(60) |
NOT NULL |
Họ và tên |
ngay_sinh |
DATE |
NOT NULL |
Ngày sinh |
gioi_tinh |
VARCHAR(3) |
CHECK IN ('Nam','Nữ') |
Giới tính |
dia_chi |
VARCHAR(120) |
cho phép NULL |
Địa chỉ nhà |
ma_lop |
CHAR(3) |
FK → lop, NOT NULL |
Lớp đang học |
| Cột | Kiểu | Ràng buộc | Ý nghĩa |
|---|---|---|---|
ma_ph |
CHAR(5) |
PK | Mã phụ huynh, dạng PH001 |
ho_ten |
VARCHAR(60) |
NOT NULL |
Họ và tên |
so_dien_thoai |
VARCHAR(15) |
cho phép NULL |
Số liên lạc |
quan_he |
VARCHAR(10) |
CHECK IN ('Bố','Mẹ','Ông','Bà','Khác') |
Quan hệ với học sinh |
ma_hs |
CHAR(5) |
FK → hoc_sinh, ON DELETE CASCADE |
Con em |
| Cột | Kiểu | Ràng buộc | Ý nghĩa |
|---|---|---|---|
ma_mon |
CHAR(4) |
PK | Mã môn, dạng MH01 |
ten_mon |
VARCHAR(30) |
UNIQUE | Tên môn |
so_tiet_tuan |
SMALLINT |
CHECK 1..10 |
Số tiết mỗi tuần |
| Cột | Kiểu | Ràng buộc | Ý nghĩa |
|---|---|---|---|
ma_gv |
CHAR(4) |
PK + FK → giao_vien |
Giáo viên |
ma_mon |
CHAR(4) |
PK + FK → mon_hoc |
Môn được phân công |
ma_lop |
CHAR(3) |
PK + FK → lop |
Lớp được dạy |
hoc_ky |
SMALLINT |
PK, CHECK IN (1,2) |
Học kỳ |
Đây là ví dụ khoá chính phức hợp gồm 4 cột, và là hiện thực của một mối quan hệ bậc ba — xem Bài 8 và Bài 12.
| Cột | Kiểu | Ràng buộc | Ý nghĩa |
|---|---|---|---|
ma_diem |
SERIAL |
PK | Khoá nhân tạo, tự tăng |
ma_hs |
CHAR(5) |
FK → hoc_sinh |
Học sinh |
ma_mon |
CHAR(4) |
FK → mon_hoc |
Môn học |
hoc_ky |
SMALLINT |
CHECK IN (1,2) |
Học kỳ |
loai_diem |
VARCHAR(10) |
CHECK IN ('15 phút','1 tiết','Học kỳ') |
Loại bài kiểm tra |
diem_so |
NUMERIC(4,2) |
CHECK 0..10 |
Điểm — dùng NUMERIC chứ không FLOAT, xem Bài 22 |
ngay_nhap |
DATE |
mặc định hôm nay | Ngày nhập điểm |
| Cột | Kiểu | Ràng buộc | Ý nghĩa |
|---|---|---|---|
ma_sach |
CHAR(4) |
PK | Mã sách, dạng S001 |
ten_sach |
VARCHAR(100) |
NOT NULL |
Tên sách |
tac_gia |
VARCHAR(60) |
cho phép NULL |
Tác giả |
nam_xuat_ban |
SMALLINT |
CHECK 1800..2100 |
Năm xuất bản |
so_luong |
SMALLINT |
CHECK >= 0 |
Số bản trong kho |
| Cột | Kiểu | Ràng buộc | Ý nghĩa |
|---|---|---|---|
ma_muon |
SERIAL |
PK | Khoá nhân tạo |
ma_hs |
CHAR(5) |
FK → hoc_sinh |
Người mượn |
ma_sach |
CHAR(4) |
FK → sach |
Sách được mượn |
ngay_muon |
DATE |
NOT NULL |
Ngày mượn |
ngay_tra_du_kien |
DATE |
CHECK >= ngay_muon |
Hạn phải trả |
ngay_tra_thuc_te |
DATE |
cho phép NULL |
NULL = chưa trả |
| Cột | Kiểu | Ràng buộc | Ý nghĩa |
|---|---|---|---|
ma_dd |
SERIAL |
PK | Khoá nhân tạo |
ma_hs |
CHAR(5) |
FK → hoc_sinh |
Học sinh |
ngay |
DATE |
NOT NULL |
Ngày điểm danh |
trang_thai |
VARCHAR(12) |
CHECK IN ('Có mặt','Vắng có phép','Vắng không phép','Đi muộn') |
Tình trạng |
ly_do |
VARCHAR(100) |
cho phép NULL |
Lý do vắng, nếu có |
Cặp (ma_hs, ngay) là UNIQUE: một học sinh chỉ được điểm danh một lần mỗi ngày.
Dữ liệu này có được kiểm tra không?¶
Có. Mỗi lần khóa học được cập nhật, GitHub Actions dựng một PostgreSQL 16 mới tinh, nạp cả ba file SQL, kiểm tra số dòng, rồi chạy lại toàn bộ câu lệnh SQL xuất hiện trong 51 bài học. Nếu có một câu sai, build đỏ và bài học không được xuất bản.
Nói cách khác: mọi câu SQL bạn đọc trong khóa học này đều đã chạy thật trên PostgreSQL.