Bài 10: Các hàm tính toán gộp (Aggregate Functions) và Gom nhóm (GROUP BY)
I. Mục đích (Objectives)
Sau bài học này, người đọc sẽ:
- Sử dụng thành thạo 5 hàm thống kê cơ bản: COUNT (Đếm), SUM (Tổng), AVG (Trung bình), MAX (Cao nhất), MIN (Thấp nhất).
- Hiểu và áp dụng được mệnh đề GROUP BY để thống kê dữ liệu theo từng nhóm (ví dụ: Điểm trung bình theo từng lớp).
- Phân biệt rõ sự khác nhau giữa WHERE và HAVING.
- Biết cách làm tròn số liệu báo cáo cho đẹp mắt.
II. Yêu cầu (Prerequisites)
- Cơ sở dữ liệu đã có dữ liệu ở cả bảng HocSinh và LopHoc (kết quả từ Bài 9: Truy vấn nhiều bảng (JOINs) – Sức mạnh của sự liên kết).
III. Nội dung chi tiết (Detailed Content)
1. Các hàm tính toán gộp (Aggregate Functions)
Thay vì trả về từng dòng dữ liệu lẻ tẻ, các hàm này sẽ “gộp” nhiều dòng lại thành một con số duy nhất.
a. Đếm số lượng (COUNT)
Câu hỏi: Trường ta có tổng cộng bao nhiêu học sinh?
SELECT COUNT(*) AS tong_so_hoc_sinh
FROM HocSinh;
b. Tính trung bình (AVG), Cao nhất (MAX), Thấp nhất (MIN)
Câu hỏi: Điểm cao nhất, thấp nhất và điểm trung bình của toàn trường là bao nhiêu?
SELECT
MAX(diem_tb) AS diem_cao_nhat,
MIN(diem_tb) AS diem_thap_nhat,
AVG(diem_tb) AS diem_trung_binh_toan_truong
FROM HocSinh;
Mẹo nhỏ: Hàm AVG thường trả về số lẻ rất dài (ví dụ: 8.1233333). Bạn có thể lồng thêm hàm ROUND để làm tròn: ROUND(AVG(diem_tb), 2) (làm tròn 2 số thập phân).
2. Gom nhóm dữ liệu với GROUP BY
Đây là phần thú vị nhất. Thay vì tính cho “toàn trường”, Hiệu trưởng muốn xem báo cáo theo từng lớp.
Nguyên tắc: Khi bạn muốn thống kê “theo cái gì”, thì hãy đặt cái đó sau GROUP BY.
Cú pháp:
SELECT cot_muon_gom_nhom, HAM_THONG_KE(cot_so_lieu)
FROM ten_bang
GROUP BY cot_muon_gom_nhom;
Ví dụ 1: Đếm số học sinh của từng lớp
Lưu ý: Chúng ta cần JOIN bảng LopHoc để hiện tên lớp cho dễ nhìn.
SELECT
l.ten_lop,
COUNT(h.id) AS si_so
FROM HocSinh h
JOIN LopHoc l ON h.ma_lop_id = l.id_lop
GROUP BY l.ten_lop;
Kết quả:
- 10A1: 45
- 10A2: 42
Ví dụ 2: Tính điểm trung bình của từng lớp
Để xem lớp nào học giỏi nhất.
SELECT
l.ten_lop,
ROUND(AVG(h.diem_tb), 1) AS diem_tb_lop
FROM HocSinh h
JOIN LopHoc l ON h.ma_lop_id = l.id_lop
GROUP BY l.ten_lop;
3. Lọc dữ liệu sau khi gom nhóm (HAVING)
Vấn đề: Hãy tìm ra những lớp có điểm trung bình trên 8.0 (Lớp Tiên Tiến).
Nhiều bạn sẽ quen tay dùng WHERE:
-- SAI LẦM PHỔ BIẾN
SELECT ten_lop, AVG(diem_tb) FROM ... GROUP BY ... WHERE AVG(diem_tb) > 8.0; -- LỖI CÚ PHÁP
Tại sao lỗi?
- WHERE: Lọc dữ liệu thô TRƯỚC khi gom nhóm (lọc từng học sinh).
- HAVING: Lọc dữ liệu thống kê SAU khi đã gom nhóm (lọc từng lớp).
Câu lệnh đúng:
SELECT
l.ten_lop,
ROUND(AVG(h.diem_tb), 1) AS diem_tb_lop
FROM HocSinh h
JOIN LopHoc l ON h.ma_lop_id = l.id_lop
GROUP BY l.ten_lop
HAVING AVG(h.diem_tb) >= 8.0;
IV. Tổng kết (Summary)
Hôm nay bạn đã hoàn thành kỹ năng viết báo cáo số liệu:
- Dùng COUNT, SUM, AVG… để tính toán.
- Dùng GROUP BY để chia nhỏ số liệu theo nhóm (Lớp, Tháng, Năm…).
- Dùng HAVING để lọc các nhóm đạt chuẩn.
Công thức tổng quát cho một câu SQL “khủng” (thứ tự chạy lệnh): FROM + JOIN -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY -> LIMIT.
Chúc mừng bạn đã hoàn thành Giai đoạn 3. Hệ thống kiến thức SQL của bạn đã khá hoàn chỉnh để làm việc.
Ở bài tiếp theo, chúng ta sẽ bước sang Giai đoạn 4: Tối ưu hóa & Tự động hóa. Chúng ta sẽ học cách làm cho câu truy vấn chạy nhanh hơn và thông minh hơn. Hẹn gặp lại các bạn trong Bài 11: View, Index và Trigger.
