CodeGym /Các khóa học /SQL SELF /Lọc dữ liệu đã được tổng hợp với HAVING

Lọc dữ liệu đã được tổng hợp với HAVING

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

Có một thứ tụi mình chưa bàn tới, đó là làm sao để lọc các nhóm sau khi đã dùng aggregate? Đôi khi mình đâu cần tất cả các khoa — chỉ cần những khoa có hơn trăm sinh viên thôi. Hoặc mình muốn xem những phòng ban nào có lương trung bình trên 50,000. Hôm nay tụi mình sẽ làm quen với việc lọc dữ liệu đã tổng hợp bằng HAVING.

Tại sao lại cần HAVING, trong khi đã có WHERE? Sao không đặt WHERE sau GROUP BY luôn nhỉ :)

Không đơn giản vậy đâu! Đầu tiên, thứ tự các toán tử trong SQL là cố định và WHERE sẽ chạy trước GROUP BY.

Vậy có thể đẩy nó xuống sau GROUP BY không?

Cũng không luôn! Thường thì mình cần lọc các dòng trong bảng trước khi nhóm. Sau đó mới nhóm dữ liệu đã lọc. Rồi cuối cùng loại bỏ những nhóm không cần thiết sau khi đã nhóm.

Vậy thì sao không copy WHERE, đặt tên là HAVING rồi để nó sau GROUP BY?

Chuẩn luôn! Làm vậy đi! :)

Khác biệt giữa HAVINGWHERE

WHERE lọc dòng trước khi nhóm.

Ví dụ như bạn chọn bánh theo vị: dâu và socola thì giữ lại, còn lại bỏ qua. Đó là việc của WHERE.

HAVING lọc sau khi dữ liệu đã được nhóm và các hàm tổng hợp đã làm xong việc của mình.

Ví dụ, bạn đã nhóm bánh theo bàn, đếm số lượng bánh trên mỗi bàn và giờ chỉ muốn giữ lại những bàn có hơn ba cái bánh.

Vậy nên, HAVING dùng để lọc dữ liệu ở cấp nhóm.

Cú pháp của HAVING

Cú pháp gần giống WHERE, nhưng nó hoạt động hơi khác chút:

SELECT cột, hàm_tổng_hợp
FROM bảng
GROUP BY cột
HAVING điều_kiện;

Các bước thực hiện:

  1. Đầu tiên các dòng được lọc qua WHERE.
  2. Sau đó dữ liệu được nhóm bằng GROUP BY.
  3. Các hàm tổng hợp được áp dụng lên kết quả nhóm.
  4. Cuối cùng, kết quả sẽ được lọc tiếp bằng HAVING.

Ví dụ sử dụng HAVING

Ví dụ 1: Lọc các khoa có nhiều sinh viên

Bạn muốn biết khoa nào trong trường đại học có hơn 100 sinh viên. Giả sử có bảng students:

id name faculty
1 Alice Engineering
2 Bob Engineering
3 Charlie Arts
4 Daisy Business
5 ... ...

Câu truy vấn:

SELECT faculty, COUNT(*) AS student_count
FROM students
GROUP BY faculty
HAVING COUNT(*) > 100;

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

  • Đầu tiên mình nhóm sinh viên theo cột faculty bằng GROUP BY.
  • Sau đó hàm tổng hợp COUNT(*) đếm số sinh viên ở mỗi khoa.
  • Cuối cùng, HAVING loại bỏ các khoa có 100 sinh viên trở xuống.

Kết quả:

faculty student_count
Engineering 150
Arts 120

Ví dụ 2: Phòng ban có lương trung bình cao

Bạn muốn tìm những phòng ban mà lương trung bình của nhân viên lớn hơn 50,000. Giả sử có bảng employees:

id name department salary
1 Alice IT 60000
2 Bob HR 45000
3 Charlie IT 70000
4 Daisy HR 52000
5 ... ... ...

Câu truy vấn:

SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 50000;

Kết quả:

department avg_salary
IT 65000

Lưu ý: HAVING làm việc với kết quả đã được tính sau GROUP BY.

Thứ tự thực hiện WHERE, GROUP BYHAVING

Lọc bằng WHEREHAVING diễn ra ở các bước khác nhau. Để hiểu rõ hơn, cùng xem quy trình từng bước của truy vấn:

  1. WHERE: lọc dòng.

    Ở bước này, tất cả các dòng của bảng được xử lý. Nếu dòng không thỏa điều kiện WHERE, nó sẽ không được xử lý tiếp.

  2. GROUP BY: nhóm dòng.

    Sau khi lọc, các dòng được gom thành nhóm dựa trên các cột chỉ định trong GROUP BY.

  3. Hàm tổng hợp:

    Các hàm tổng hợp như COUNT(), AVG(), SUM()... được áp dụng lên dữ liệu đã nhóm.

  4. HAVING: lọc nhóm.

    Bước này chỉ xử lý kết quả của các aggregate. Điều kiện HAVING chỉ áp dụng cho nhóm.

Đặc điểm của HAVING

Đặc điểm 1: Làm việc với aggregate

Điểm khác biệt lớn nhất giữa HAVINGWHEREHAVING làm việc với các hàm tổng hợp. Ví dụ:

SELECT department, AVG(salary)
FROM employees
GROUP BY department
HAVING AVG(salary) > 50000;

Trong truy vấn này AVG(salary) không thể dùng trong WHERE, vì WHERE xử lý dòng trước khi nhóm. Truy vấn kiểu như:

SELECT department, AVG(salary)
FROM employees
WHERE AVG(salary) > 50000
GROUP BY department;

sẽ báo lỗi: aggregate functions are not allowed in WHERE.

Đặc điểm 2: Lọc mà không cần GROUP BY

Bạn có thể dùng HAVING ngay cả khi không có GROUP BY. Khi đó truy vấn sẽ coi như có một nhóm duy nhất — tất cả các bản ghi:

SELECT AVG(salary) AS avg_salary
FROM employees
HAVING AVG(salary) > 50000;

Ví dụ thực tế

Giả sử có một cửa hàng và bảng bán hàng sales:

id product_id sales_amount
1 101 200.00
2 102 300.00
3 101 400.00
4 103 150.00

Câu truy vấn: tìm sản phẩm có tổng doanh số lớn hơn 500.

SELECT product_id, SUM(sales_amount) AS total_sales
FROM sales
GROUP BY product_id
HAVING SUM(sales_amount) > 500;

Kết quả:

product_id total_sales
101 600.00

Lỗi thường gặp

Dùng aggregate trong WHERE:

Ví dụ:

SELECT department, AVG(salary)
FROM employees
WHERE AVG(salary) > 50000
GROUP BY department;

Lỗi: không được dùng hàm tổng hợp trong WHERE.

Lỗi với NULL:

Nếu dữ liệu có NULL, việc lọc có thể ra kết quả bất ngờ. Ví dụ:

SELECT department, SUM(salary)
FROM employees
GROUP BY department
HAVING SUM(salary) > 0;

Nếu cột salary chỉ toàn NULL, kết quả có thể là 0 hoặc rỗng.

Chúc mừng nhé. Đến đây là bạn đã tự tin lọc dữ liệu tổng hợp rồi! Đừng quên, HAVING là chìa khóa để phân tích ở cấp nhóm, nơi mà WHERE thông thường không đủ dùng nữa đâu.

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