Nguyễn Đình Cường
0 comment
09 Nov, 2026
Biết viết SELECT chưa đủ, bạn còn cần biết chia dữ liệu vào các bảng hợp lý và nối chúng lại. Bài này dẫn bạn thiết kế một cơ sở dữ liệu quản lý lớp học nhỏ gồm sinh viên, môn học và điểm số, từ lúc tạo bảng đến các truy vấn JOIN và thống kê.
Ta cần lưu: danh sách sinh viên, danh sách môn học, và điểm của từng sinh viên ở từng môn. Một sinh viên học nhiều môn, một môn có nhiều sinh viên, đó là quan hệ nhiều-nhiều. Cách giải chuẩn là thêm một bảng trung gian grades mỗi dòng ghi điểm của một sinh viên ở một môn. Nếu nhồi tất cả vào một bảng, bạn sẽ lặp lại tên sinh viên và tên môn khắp nơi, dễ sai lệch khi cập nhật. Việc tách ra để mỗi thông tin chỉ nằm ở một chỗ gọi là chuẩn hóa.
Các lệnh dưới đây dùng cú pháp MySQL. Với PostgreSQL, bạn thay AUTO_INCREMENT bằng GENERATED ... AS IDENTITY hoặc SERIAL.
CREATE DATABASE school CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE school;
CREATE TABLE students (
id INT AUTO_INCREMENT PRIMARY KEY,
full_name VARCHAR(100) NOT NULL,
email VARCHAR(150) UNIQUE,
birth_year SMALLINT
) ENGINE=InnoDB;
CREATE TABLE subjects (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
credits TINYINT NOT NULL DEFAULT 3
) ENGINE=InnoDB;
CREATE TABLE grades (
id INT AUTO_INCREMENT PRIMARY KEY,
student_id INT NOT NULL,
subject_id INT NOT NULL,
score DECIMAL(4,2) NOT NULL,
CONSTRAINT fk_grades_student FOREIGN KEY (student_id) REFERENCES students(id) ON DELETE CASCADE,
CONSTRAINT fk_grades_subject FOREIGN KEY (subject_id) REFERENCES subjects(id) ON DELETE CASCADE,
CONSTRAINT uq_student_subject UNIQUE (student_id, subject_id),
CONSTRAINT chk_score CHECK (score >= 0 AND score <= 10)
) ENGINE=InnoDB;Một vài điểm quan trọng: khóa chính định danh duy nhất từng dòng; khóa ngoại đảm bảo điểm chỉ gắn với sinh viên và môn có thật; ràng buộc UNIQUE (student_id, subject_id) ngăn nhập hai điểm cho cùng cặp; CHECK chặn điểm sai miền giá trị (MySQL từ bản 8.0.16 mới thực thi ràng buộc này). Quy tắc ON DELETE CASCADE tự xóa điểm khi xóa sinh viên, hãy cân nhắc kỹ trước khi dùng cho dữ liệu quan trọng.
INSERT INTO students (full_name, email, birth_year) VALUES
('Nguyễn An', 'an@example.com', 2004),
('Trần Bình', 'binh@example.com', 2003),
('Lê Cường', 'cuong@example.com', 2004);
INSERT INTO subjects (name, credits) VALUES
('Lập trình căn bản', 3),
('Cơ sở dữ liệu', 3),
('Tiếng Anh chuyên ngành', 2);
INSERT INTO grades (student_id, subject_id, score) VALUES
(1, 1, 8.5), (1, 2, 9.0),
(2, 1, 7.0), (2, 2, 6.5), (2, 3, 8.0),
(3, 1, 9.5);Để xem điểm kèm tên sinh viên và tên môn, nối ba bảng:
SELECT s.full_name, sub.name AS subject, g.score
FROM grades g
JOIN students s ON s.id = g.student_id
JOIN subjects sub ON sub.id = g.subject_id
ORDER BY s.full_name, sub.name;JOIN (hay INNER JOIN) chỉ giữ các dòng khớp ở cả hai phía. Nếu muốn liệt kê cả sinh viên chưa có điểm nào, dùng LEFT JOIN:
SELECT s.full_name, COUNT(g.id) AS so_mon_co_diem
FROM students s
LEFT JOIN grades g ON g.student_id = s.id
GROUP BY s.id, s.full_name;Điểm trung bình theo sinh viên, chỉ lấy người có trung bình từ 8 trở lên:
SELECT s.full_name, ROUND(AVG(g.score), 2) AS diem_tb
FROM students s
JOIN grades g ON g.student_id = s.id
GROUP BY s.id, s.full_name
HAVING AVG(g.score) >= 8
ORDER BY diem_tb DESC;Hãy ghi nhớ khác biệt: WHERE lọc từng dòng trước khi gom nhóm, còn HAVING lọc các nhóm sau khi đã tính hàm tổng hợp. Và điểm trung bình theo môn học:
SELECT sub.name, COUNT(*) AS so_sv, MAX(g.score) AS cao_nhat, MIN(g.score) AS thap_nhat
FROM subjects sub
JOIN grades g ON g.subject_id = sub.id
GROUP BY sub.id, sub.name;Khóa ngoại thường được MySQL tự đánh chỉ mục. Với các cột hay dùng trong WHERE hoặc JOIN khác, hãy tạo chỉ mục. Dùng EXPLAIN trước câu truy vấn để xem MySQL thực thi thế nào:
CREATE INDEX idx_students_name ON students(full_name);
EXPLAIN SELECT * FROM students WHERE full_name = 'Nguyễn An';SELECT * thường xuyên trong ứng dụng thật.CASE WHEN dựa trên điểm trung bình.teachers và khóa ngoại từ subjects sang giảng viên.Ba bảng, vài khóa ngoại và các truy vấn JOIN, GROUP BY đã đủ mô hình hóa nhiều bài toán thực tế. Hãy tự thử thêm bảng giảng viên, học kỳ và xếp loại học lực để mở rộng.
Nếu muốn học SQL và thiết kế cơ sở dữ liệu có hệ thống, bạn có thể xem các khóa học tại codebem.com.
Nguyễn Đình Cường
0 comment