CodeGym /Các khóa học /SQL SELF /Ứng dụng subquery trong HAVING để lọc dữ liệu đã được tổn...

Ứng dụng subquery trong HAVING để lọc dữ liệu đã được tổng hợp

SQL SELF
Mức độ , Bài học
Có sẵn

Đôi khi tụi mình không chỉ cần group dữ liệu rồi lọc kết quả, mà còn phải xét thêm logic khác — ví dụ như so sánh điểm trung bình của sinh viên trong nhóm với một tiêu chí bên ngoài nào đó. Đây là lúc HAVING kết hợp với subquery xuất hiện — một công cụ cực mạnh giúp bạn đưa ra quyết định thông minh ngay trong SQL query.

Nhớ lại HAVING

Cùng tập trung vào subquery dùng chung với HAVING để lọc dữ liệu ở mức tổng hợp. Tại sao lại cần? Nếu WHERE cho phép lọc từng dòng riêng lẻ, thì HAVING lại áp dụng cho dữ liệu đã được group — đây là một level phân tích khác, mở rộng khả năng của bạn.

Trước khi đi sâu vào kết hợp subquery và HAVING, cùng ôn lại HAVING là gì và nó khác WHERE ra sao nhé.

  • WHERE lọc dòng trước khi thực hiện group (GROUP BY).
  • HAVING lọc dữ liệu sau khi đã tổng hợp, tức là dữ liệu đã được group rồi.

Ví dụ bạn đang phân tích sinh viên và điểm số của họ. Dùng WHERE bạn có thể loại bỏ sinh viên có điểm thấp nhất định, còn HAVING thì cho phép loại cả nhóm sinh viên dựa trên điểm trung bình hoặc điểm cao nhất của nhóm đó.

Ví dụ dữ liệu

Đây là bảng ví dụ về sinh viên:

Bảng students:

student_id student_name department grade
1 Alex Physics 80
2 Maria Physics 85
3 Dan Math 90
4 Lisa Math 60
5 John History 70

Ví dụ dùng HAVING (không có subquery)

SELECT department, AVG(grade) AS avg_grade
FROM students
GROUP BY department
HAVING AVG(grade) > 75;

Kết quả:

department avg_grade
Physics 82.5
Math 75.0

Khoa "History" không xuất hiện trong kết quả vì điểm trung bình của nó dưới 75. Đơn giản ha? Giờ thêm chút "ma thuật" với subquery nhé. Ở ví dụ tiếp theo, tụi mình có thể lọc dựa trên so sánh với điểm trung bình toàn trường.

Subquery trong HAVING

Subquery trong HAVING là cách tuyệt vời để tăng độ linh hoạt khi lọc dữ liệu tổng hợp. Bạn có thể so sánh các giá trị tổng hợp như điểm trung bình hoặc điểm cao nhất với giá trị tính toán từ nơi khác trong database. Nói đơn giản, bạn có thể kiểm tra: "Kết quả của mình có hơn mức trung bình không nhỉ?"

Ví dụ: lọc khoa theo điểm trung bình

Giả sử bạn muốn tìm những khoa mà sinh viên học tốt hơn các khoa khác — tức là điểm trung bình của khoa đó cao hơn trung bình toàn trường.

Dữ liệu của tụi mình như sau:

Bảng students:

student_id student_name department grade
1 Alex Physics 80
2 Maria Physics 85
3 Dan Math 90
4 Lisa Math 60
5 John History 70

Đầu tiên lấy điểm trung bình của tất cả sinh viên:

SELECT AVG(grade) AS university_avg
FROM students;

Giờ áp dụng subquery trong HAVING:

SELECT department, AVG(grade) AS avg_grade
FROM students
GROUP BY department
HAVING AVG(grade) > (SELECT AVG(grade) FROM students);

Kết quả:

department avg_grade
Physics 82.5

Chuyện gì đang xảy ra ở đây?

  1. Subquery (SELECT AVG(grade) FROM students) tính điểm trung bình toàn trường — ở đây là 77.
  2. Query chính group sinh viên theo khoa và tính điểm trung bình cho từng khoa.
  3. HAVING so sánh điểm trung bình của khoa với trung bình toàn trường và chỉ lấy những khoa cao hơn.

So sánh dùng WHEREHAVING

Để hiểu rõ hơn, giả sử bạn muốn chọn những sinh viên có điểm cao hơn trung bình. Cái này chỉ cần WHERE thôi:

SELECT name, grade
FROM students
WHERE grade > (SELECT AVG(grade) FROM students);

Kết quả (dùng bảng ở ví dụ trên):

name grade
Alex 80
Maria 85
Dan 90

Còn nếu bạn muốn xem khoa nào có điểm trung bình cao hơn trung bình toàn trường thì không thể thiếu HAVING — vì bạn đang lọc theo nhóm chứ không phải từng dòng:

SELECT department, AVG(grade) AS avg_grade
FROM students
GROUP BY department
HAVING AVG(grade) > (SELECT AVG(grade) FROM students);

Kết quả:

department avg_grade
Physics 82.5

Tóm lại:

  • WHERE làm việc với từng dòng trước khi group.
  • HAVING lọc nhóm sau khi đã tổng hợp.

Ví dụ: làm việc với nhiều aggregate

Cùng xem thêm một trường hợp nữa. Giả sử bạn có bảng students lưu điểm và khoa của sinh viên:

Bảng students:

name grade department
Alex 80 Physics
Maria 85 Physics
Dan 90 Math
Olga 95 Math
Ivan 70 History
Nina 75 History

Giờ mình muốn tìm các khoa mà:

  1. Điểm trung bình của sinh viên cao hơn trung bình toàn trường.
  2. Điểm cao nhất của khoa đó trên 90.

Viết query như sau:

SELECT department, AVG(grade) AS avg_grade, MAX(grade) AS max_grade
FROM students
GROUP BY department
HAVING AVG(grade) > ( SELECT AVG(grade) FROM students )
   AND MAX(grade) > 90;

Ý nghĩa của query này:

  • AVG(grade) > (SELECT AVG(grade) FROM students) — kiểm tra khoa này có điểm trung bình cao hơn các khoa khác không.
  • MAX(grade) > 90 — nghĩa là có ai đó trong khoa đạt điểm xuất sắc.

Kết quả:

department avg_grade max_grade
Math 92.5 95

Khoa "Math" là khoa duy nhất vừa có điểm trung bình cao hơn toàn trường, vừa có sinh viên đạt trên 90 điểm.

Ví dụ: chọn nhóm có độ lệch nhỏ nhất

Giả sử bạn muốn tìm các nhóm mà chênh lệch giữa điểm cao nhất và thấp nhất của sinh viên nhỏ hơn chênh lệch toàn trường.

Đây là bảng students mình sẽ dùng:

name grade department
Alex 80 Physics
Maria 85 Physics
Dan 90 Math
Olga 95 Math
Ivan 70 History
Nina 75 History

Chia nhỏ bài toán ra:

  1. Đầu tiên tính chênh lệch max-min toàn trường:
    SELECT MAX(grade) - MIN(grade) AS range_university
    FROM students;
    
  2. Giờ viết query chính và kết hợp với subquery này:
SELECT department, MAX(grade) - MIN(grade) AS range_department
FROM students
GROUP BY department
HAVING (MAX(grade) - MIN(grade)) < ( SELECT MAX(grade) - MIN(grade) FROM students );

Kết quả query:

department range_department
Physics 5
Math 5

Nhóm "Physics" và "Math" có điểm số ổn định hơn — độ lệch nhỏ hơn toàn trường.

Tối ưu hóa query với HAVING và subquery

Nhớ rằng subquery lồng nhau có thể ảnh hưởng mạnh đến hiệu năng, nhất là với database lớn. Một vài tips cho bạn:

Dùng index. Nếu subquery chạy trên cột nào có trong WHERE hoặc JOIN, hãy chắc chắn cột đó đã có index.

Tránh dữ liệu trung gian quá nhiều. Nếu subquery trả về quá nhiều kết quả tạm, hãy chia nhỏ ra hoặc dùng bảng tạm.

Profile query với EXPLAIN. Luôn kiểm tra PostgreSQL thực thi query ra sao. Nếu subquery bị chạy nhiều lần, hãy nghĩ cách tối ưu lại.

So sánh với CTE. Đôi khi dùng WITH (Common Table Expressions) sẽ nhanh hơn và dễ đọc hơn. Nhưng cái này để bài sau nói tiếp :P

Kết hợp subquery, HAVINGGROUP BY

Dùng subquery trong HAVING bạn có thể xây dựng filter phức tạp hơn, nhất là khi cần xét đồng thời nhiều aggregate, giá trị trung bình và các chỉ số khác. Tất cả giúp bạn tìm ra insight thú vị từ dữ liệu thực tế.

Ví dụ: so sánh khoa theo điểm trung bình và số lượng sinh viên

Giả sử bạn muốn chọn các khoa mà:

  1. Điểm trung bình cao hơn trung bình toàn trường.
  2. Số sinh viên nhiều hơn khoa có điểm trung bình thấp nhất.

Đây là bảng students gốc:

name grade department
Alex 80 Physics
Maria 85 Physics
Dan 90 Math
Olga 95 Math
Ivan 70 History
Nina 75 History
Oleg 60 History

Query:

SELECT department, AVG(grade) AS avg_grade, COUNT(*) AS student_count
FROM students
GROUP BY department
HAVING AVG(grade) > ( SELECT AVG(grade) FROM students )
   AND COUNT(*) > (
       SELECT COUNT(*)
       FROM students
       GROUP BY department
       ORDER BY AVG(grade)
       LIMIT 1
   );

Query này cho thấy khả năng kết hợp subquery trong HAVINGGROUP BY để phân tích nhiều tiêu chí cùng lúc. Kết quả:

department avg_grade student_count
Physics 82.5 2
Math 92.5 2

Khoa History không xuất hiện vì có điểm trung bình thấp nhất và ít sinh viên nhất. Physics và Math — đều trên trung bình cả về điểm lẫn số lượng.

Lỗi thường gặp và cách tránh

Lỗi với NULL. Nếu dữ liệu có NULL, subquery với HAVING có thể trả về kết quả bất ngờ. Hãy dùng COALESCE để xử lý:

SELECT AVG(grade)
FROM students 
WHERE grade IS NOT NULL;

Dữ liệu dư thừa trong subquery. Nếu subquery trả về quá nhiều kết quả, hiệu năng sẽ giảm. Luôn làm rõ điều kiện trong subquery.

Hiểu sai thứ tự thực thi. Nhớ rằng HAVING chạy sau khi group, còn subquery có thể chạy trước query chính.

Thiếu index. Nếu cột dùng trong subquery không có index, query sẽ rất chậm.

Subquery trong HAVING mở ra rất nhiều khả năng phân tích dữ liệu ở mức tổng hợp. Bạn có thể lọc nhóm theo điều kiện phức tạp, so sánh kết quả giữa các nhóm và tạo truy vấn phân tích nâng cao. Chúc mừng nhé, giờ bạn đã sẵn sàng áp dụng kiến thức này vào dự án thực tế rồi đó!

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION