Lấy Top 3 Điểm Cao Nhất Cho Mỗi Môn Học Trong MySQL

Để trích xuất ba học sinh có điểm cao nhất trong từng môn học từ bảng điểm, MySQL cung cấp nhiều cách tiếp cận — từ phương pháp truyền thống dùng subquery đến kỹ thuật hiện đại dựa trên hàm cửa sổ. Dưới đây là giải pháp tối ưu, rõ ràng và tương thích với MySQL 8.0 trở lên.

Cấu trúc dữ liệu mẫu

Giả sử ta có bảng scores với các cột:

  • student_id: mã số học sinh (INT)
  • subject: tên môn học (VARCHAR)
  • score: điểm số (DECIMAL(5,2))

Truy vấn chính xác bằng hàm cửa sổ

Sử dụng ROW_NUMBER() để đánh số thứ hạng riêng biệt cho từng môn học theo thứ tự điểm giảm dần:

SELECT 
  student_id,
  subject,
  score
FROM (
  SELECT 
    student_id,
    subject,
    score,
    ROW_NUMBER() OVER (
      PARTITION BY subject 
      ORDER BY score DESC, student_id ASC
    ) AS position
  FROM scores
) AS ranked
WHERE position <= 3;

Chú thích quan trọng:

  • PARTITION BY subject chia dữ liệu thành các nhóm theo môn học.
  • ORDER BY score DESC, student_id ASC đảm bảo thứ hạng ưu tiên điểm cao trước; nếu có điểm trùng nhau thì ưu tiên học sinh có student_id nhỏ hơn để đảm bảo tính ổn định và dễ kiểm soát.
  • Bảng con ranked chứa toàn bộ bản ghi kèm vị trí xếp hạng — lọc ngoài cùng chỉ giữ lại các bản ghi có position ≤ 3.

Phương án thay thế cho phiên bản MySQL cũ hơn (< 8.0)

Nếu hệ thống đang chạy MySQL 5.7 hoặc thấp hơn, không hỗ trợ hàm cửa sổ, có thể dùng subquery lồng ghép với đếm tự động:

SELECT s1.student_id, s1.subject, s1.score
FROM scores s1
WHERE (
  SELECT COUNT(*)
  FROM scores s2
  WHERE s2.subject = s1.subject 
    AND s2.score > s1.score
) < 3
ORDER BY s1.subject, s1.score DESC;

Cách này so sánh từng bản ghi s1 với tất cả bản ghi cùng môn học s2, đếm số điểm cao hơn — nếu ít hơn 3 điểm cao hơn, tức là s1 nằm trong top 3.

Lưu ý về hiệu năng

Đối với bảng lớn, hãy đảm bảo có chỉ mục phù hợp:

CREATE INDEX idx_subject_score ON scores(subject, score DESC);

Chỉ mục ghép này giúp tăng tốc đáng kể cả hai phương án trên khi thực hiện phân vùng theo môn và sắp xếp theo điểm.

Thẻ: mysql window-functions row-number sql-performance subquery

Đăng vào ngày 20 tháng 7 lúc 23:25