Câu 1Khi bạn chỉ định GROUP BY name, kết quả có bao nhiêu hàng (bảng score / 10 tên khác nhau)?
GROUP BY và HAVING — Tổng hợp theo nhóm
Học SQL GROUP BY và HAVING: tổng hợp theo từng nhóm, lọc nhóm bằng HAVING và sự khác biệt với WHERE — thực hành trên dữ liệu điểm CSV, ngay trên trình duyệt của bạn.
Dữ liệu dùng trong bài này — bảng score
Ở bài trước bạn đã thu gọn cả một bảng thành một giá trị duy nhất. `GROUP BY` của bài này (gom nhóm — gộp các hàng có cùng giá trị vào một nhóm) thực hiện tổng hợp theo từng nhóm và trả về một hàng tổng hợp cho mỗi nhóm. Ngoài ra, `HAVING` cho phép bạn lọc kết quả tổng hợp, ví dụ "chỉ những nhóm có trung bình từ 80 trở lên".
Đề tài vẫn là bảng score như lần trước (30 hàng = 10 người × 3 môn). Gom nhóm theo name cho 10 nhóm, còn gom nhóm theo subject cho 3 nhóm.
GROUP BY — tổng hợp theo từng nhóm
Khi bạn viết GROUP BY cột, các hàng có cùng giá trị ở cột đó được gộp vào một nhóm, và các hàm tổng hợp được tính một lần cho mỗi nhóm. Với SELECT name, AVG(score) FROM score GROUP BY name;, 3 hàng có cùng tên (mỗi môn một hàng) tạo thành một nhóm, và bạn nhận được một hàng trung bình cho mỗi tên. "Cột thường" duy nhất bạn có thể đặt trong SELECT là các cột được nêu trong `GROUP BY`; bất kỳ cột nào khác đều phải bọc trong một hàm tổng hợp (vì giá trị của nó không duy nhất trong một nhóm).
AVG(score) được tính theo từng nhóm. Với 10 người, kết quả là 10 hàng.-- Số lượng / trung bình / điểm cao nhất theo môn (3 nhóm theo subject)
SELECT
subject,
COUNT(*) AS row_count,
ROUND(AVG(score), 1) AS avg_score,
MAX(score) AS top_score
FROM score
GROUP BY subject;
HAVING — lọc kết quả tổng hợp
Sau khi tổng hợp theo nhóm, đôi khi bạn muốn lọc theo giá trị tổng hợp, ví dụ "chỉ hiển thị những nhóm có trung bình từ 80 trở lên". Bạn không thể làm điều này bằng WHERE. WHERE là mệnh đề xét từng hàng riêng lẻ trước khi gom nhóm, nên một giá trị tổng hợp nhóm như AVG(score) chưa được tính. Đó là lúc dùng đến `HAVING`. HAVING được viết sau GROUP BY và xét điều kiện của nó trên kết quả tổng hợp của các nhóm. Viết HAVING AVG(score) >= 80 chỉ giữ lại những nhóm có trung bình từ 80 trở lên.
GROUP BY tổng hợp thành 10 nhóm, rồi HAVING AVG(score) >= 80 xét các nhóm đã tổng hợp và chỉ giữ lại những nhóm thỏa điều kiện.-- Chỉ những tên có trung bình vượt 85 (HAVING xét giá trị tổng hợp)
SELECT
name,
ROUND(AVG(score), 2) AS avg_score
FROM score
GROUP BY name
HAVING AVG(score) > 85;
WHERE và HAVING — lọc hàng, hay lọc nhóm
WHERE và HAVING đều là bộ lọc, nhưng chúng hoạt động ở các thời điểm khác nhau. WHERE xét từng hàng riêng lẻ và loại chúng trước khi gom nhóm. HAVING xét theo từng nhóm và loại chúng sau khi gom nhóm và tổng hợp. Thứ tự thực thi là FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY.
Bạn cũng có thể dùng cả hai cùng lúc. Khi đó luồng là "thu hẹp các hàng mục tiêu bằng WHERE, rồi gom nhóm, rồi lọc kết quả tổng hợp bằng HAVING". Ví dụ, để lấy "những người có trung bình theo tên từ 80 trở lên, chỉ xét Math và English", bạn thu hẹp hàng bằng WHERE subject IN ('Math','English') và thu hẹp nhóm bằng HAVING AVG(score) >= 80.
WHERE xét hàng trên các cột thường và loại chúng trước khi gom nhóm, còn HAVING xét nhóm trên giá trị tổng hợp (AVG, v.v.) và loại chúng sau khi gom nhóm. Điểm mấu chốt là chỗ nào mọi thứ bị loại. Ví dụ: WHERE subject='Math' (lọc hàng) và HAVING AVG(score)>=80 (lọc nhóm).-- Dùng WHERE và HAVING cùng nhau
-- Loại Science, chỉ giữ những tên có trung bình từ 80 trở lên
SELECT
name,
ROUND(AVG(score), 2) AS avg_two
FROM score
WHERE subject <> 'Science'
GROUP BY name
HAVING AVG(score) >= 80;
Kiểm tra kiến thức
Hãy trả lời từng câu hỏi một.
Câu 2Khi bạn muốn trích xuất "chỉ những người có trung bình theo tên từ 80 trở lên", điều kiện 80 trở lên đặt vào mệnh đề nào?
Câu 3Phát biểu nào mô tả đúng sự khác biệt giữa WHERE và HAVING?