Bạn đang chạy ba câu SELECT để lấy ba con số của cùng một bảng. Một câu là đủ, và nhanh hơn ba lần.
Bài toán
Báo cáo cần: tổng số bài thi, số bài đạt, và tỷ lệ đạt. Cách thông thường là chạy ba truy vấn rồi ghép tay.
Một truy vấn, một lần quét bảng
SELECT
COUNT(*) AS tong,
SUM(CASE WHEN ket_qua = 'dat' THEN 1 ELSE 0 END) AS so_dat,
ROUND(100.0 * SUM(CASE WHEN ket_qua = 'dat' THEN 1 ELSE 0 END)
/ COUNT(*), 1) AS ty_le
FROM bai_thi;
Kỹ thuật này gọi là conditional aggregation. Nó chỉ đọc bảng một lần thay vì ba lần.
Hai cách viết tương đương
SUM(CASE WHEN ket_qua = 'dat' THEN 1 ELSE 0 END)
COUNT(CASE WHEN ket_qua = 'dat' THEN 1 END)
Cách thứ hai ngắn hơn vì COUNT vốn đã bỏ qua NULL — không cần ELSE. Nhưng cách thứ nhất rõ ràng hơn cho người đọc sau này.
Lỗi thường gặp
1. Thêm ELSE vào COUNT
-- SAI: đếm cả dòng không đạt, luôn ra bằng COUNT(*)
COUNT(CASE WHEN ket_qua = 'dat' THEN 1 ELSE 0 END)
Số 0 vẫn là một giá trị, nên COUNT vẫn đếm nó. Dùng COUNT thì bỏ ELSE; dùng SUM thì giữ ELSE 0.
2. Chia số nguyên
-- Ra 0 trên PostgreSQL và MySQL
SELECT SUM(CASE WHEN ket_qua='dat' THEN 1 ELSE 0 END) / COUNT(*) FROM bai_thi;
Số nguyên chia số nguyên ra số nguyên. Nhân 100.0 hoặc ép kiểu để có phần thập phân.
3. Quên mẫu số có thể bằng 0
ROUND(100.0 * SUM(...) / NULLIF(COUNT(*), 0), 1)
NULLIF biến 0 thành NULL, và phép chia cho NULL trả về NULL thay vì báo lỗi.
Kết hợp với GROUP BY
SELECT
lop,
COUNT(*) AS si_so,
SUM(CASE WHEN diem >= 8 THEN 1 ELSE 0 END) AS gioi,
COUNT(DISTINCT CASE WHEN diem >= 8 THEN hoc_sinh_id END) AS so_hs_gioi,
ROUND(AVG(CASE WHEN diem >= 5 THEN diem END), 2) AS diem_tb_dat
FROM bang_diem
GROUP BY lop;
Dòng cuối đáng chú ý: AVG bỏ qua NULL, nên nó chỉ tính trung bình của những bài đạt — đúng ý mà không cần thêm WHERE.
Luyện tập
- LeetCode 1934 — Confirmation Rate
- LeetCode 1211 — Queries Quality and Percentage
- LeetCode 262 — Trips and Users (khó, rất đáng làm)
- LeetCode 1907 — Count Salary Categories
- HackerRank — New Companies
Tóm lại
- Nhiều chỉ số trên cùng bảng thì gộp vào một truy vấn.
SUM(CASE … ELSE 0)hoặcCOUNT(CASE …)không cóELSE.- Thêm
ELSE 0vàoCOUNTlà hỏng. - Nhân
100.0để tránh chia số nguyên. NULLIF(mẫu, 0)chặn lỗi chia cho 0.
Bạn vừa học xong đếm có điều kiện
Sẵn sàng luyện tập chưa?
Làm bài tập đếm có điều kiện trên dữ liệu thật, chấm điểm ngay khi bạn bấm chạy.
Bắt đầu luyện tập →