Hãy cùng mình đi lại một lần nữa về thứ tự thực thi các thao tác trong SQL, cũng như cái gì WHERE và HAVING có thể hoặc không thể làm được. Đây là điểm quan trọng, sau này sẽ có nhiều chi tiết nhỏ trong việc viết truy vấn SQL dựa trên nó. Bạn cần hiểu thật kỹ phần này nha.
Vậy WHERE khác HAVING ở chỗ nào, khi nào dùng cái này, khi nào dùng cái kia, và chúng liên quan với nhau ra sao? Hiểu rõ cái này sẽ giúp bạn không bị rối khi viết truy vấn và lọc dữ liệu hiệu quả hơn. Tổng kết lại kiến thức về WHERE và HAVING nhé.
WHERE là gì?
WHERE — là điều kiện dùng để lọc các dòng trước khi nhóm hoặc dùng các hàm tổng hợp. Tức là, đầu tiên sẽ chọn ra các dòng thỏa mãn điều kiện từ bảng, rồi mới thực hiện nhóm trên dữ liệu còn lại.
👉 Hình dung như bạn đi chợ chọn trái cây. WHERE — là bộ lọc giúp bạn loại bỏ táo dập, hỏng trước khi bạn bắt đầu phân loại theo kích thước hay màu sắc.
Ví dụ:
SELECT *
FROM students
WHERE age > 18;
Truy vấn này sẽ chọn tất cả sinh viên trên 18 tuổi trước khi thực hiện các thao tác khác.
HAVING là gì?
HAVING — là bộ lọc được áp dụng sau khi đã nhóm dữ liệu (GROUP BY). Nó cho phép đặt điều kiện lên các nhóm, ví dụ chỉ giữ lại những nhóm mà điểm trung bình của sinh viên lớn hơn 80.
👉 Quay lại ví dụ với táo. HAVING — là bộ lọc dùng sau khi bạn đã phân táo vào từng giỏ (nhóm). Giờ bạn chỉ quan tâm đến những giỏ (nhóm) có hơn mười quả táo thôi chẳng hạn.
Ví dụ:
SELECT giỏ, COUNT(*)
FROM táo
GROUP BY giỏ
HAVING COUNT(*) > 10;
Truy vấn này sẽ chỉ chọn những giỏ có số lượng táo lớn hơn 10.
Khác biệt chính:
| Đặc điểm | WHERE |
HAVING |
|---|---|---|
| Áp dụng | Lọc dòng trước khi nhóm | Lọc nhóm sau khi nhóm |
| Làm việc với tổng hợp | Không dùng được hàm tổng hợp | Dùng được hàm tổng hợp |
| Mục đích | Loại bỏ dòng dư thừa trước khi nhóm | Loại bỏ nhóm không thỏa điều kiện |
Thứ tự thực thi WHERE, GROUP BY và HAVING
Để hiểu rõ hơn cách WHERE và HAVING hoạt động, cùng xem thứ tự thực thi truy vấn SQL:
- Đầu tiên là
FROM, chọn dòng từ bảng. - Sau đó áp dụng
WHERE, chỉ giữ lại dòng thỏa điều kiện. - Tiếp theo là nhóm dữ liệu bằng
GROUP BY. Ta có bảng mới với các nhóm. - Áp dụng
HAVING, lọc các nhóm thỏa điều kiện. - Cuối cùng là chọn kết quả với
SELECT.
Minh họa như sau:
1. FROM → 2. WHERE → 3. GROUP BY → 4. HAVING → 5. SELECT
Ví dụ:
SELECT department, AVG(age) AS avg_age
FROM students
WHERE age > 18
GROUP BY department
HAVING AVG(age) > 20;
Ở đây sẽ diễn ra như sau:
- Chọn dòng trong bảng
studentsmàage > 18(dùngWHERE). - Các dòng còn lại được nhóm theo
department. - Tính tuổi trung bình cho từng nhóm sinh viên.
- Nhóm nào có tuổi trung bình nhỏ hơn hoặc bằng 20 sẽ bị loại (
HAVING). - Xuất kết quả.
Ví dụ kết hợp sử dụng
Ví dụ 1: Lọc trước và sau khi nhóm
Yêu cầu: tìm các khoa có hơn 5 sinh viên, chỉ tính sinh viên trên 18 tuổi.
Bảng gốc students
| id | name | department | age | gpa |
|---|---|---|---|---|
| 1 | Alex Lin | ComputerSci | 20 | 3.8 |
| 2 | Maria Chi | Math | 22 | 3.5 |
| 3 | Anna Song | ComputerSci | 19 | 4.0 |
| 4 | Otto Art | Math | 17 | 3.9 |
| 5 | Liam Park | Physics | 21 | 3.7 |
| 6 | Jane Doe | ComputerSci | 23 | 3.6 |
| 7 | Tom Brown | Math | 25 | 3.4 |
| 8 | Sara White | Math | 19 | 3.8 |
| 9 | John Smith | ComputerSci | 20 | 3.7 |
| 10 | Emily Green | Physics | 18 | 3.9 |
| 11 | Mark Blue | ComputerSci | 21 | 3.5 |
| 12 | Zoe Black | Math | 22 | 3.6 |
| 13 | Max Gray | ComputerSci | 20 | 3.9 |
| 14 | Eva Gold | Math | 23 | 3.7 |
| 15 | Nick Silver | Physics | 19 | 3.8 |
Truy vấn:
SELECT department, COUNT(*) AS student_count
FROM students
WHERE age > 18
GROUP BY department
HAVING COUNT(*) > 5;
Kết quả: -- Kết quả truy vấn
| department | student_count |
|---|---|
| ComputerSci | 6 |
Giải thích:
- Đầu tiên loại bỏ dòng mà
age <= 18(điều kiệnWHERE). - Nhóm dữ liệu theo khoa (
GROUP BY department). - Tính số sinh viên trong từng nhóm.
- Loại bỏ nhóm có số sinh viên nhỏ hơn hoặc bằng 5 (
HAVING COUNT(*) > 5).
Ví dụ 2: Lỗi khi dùng WHERE thay vì HAVING
Yêu cầu: Tìm các khoa có tuổi trung bình lớn hơn 22.
Truy vấn sai:
SELECT department, AVG(age) AS avg_age
FROM students
WHERE AVG(age) > 22
GROUP BY department;
Lỗi: SQL không cho dùng hàm tổng hợp AVG trong WHERE, vì ở bước này chưa tính xong aggregate.
Truy vấn đúng:
SELECT department, AVG(age) AS avg_age
FROM students
GROUP BY department
HAVING AVG(age) > 22;
Ở đây điều kiện AVG(age) > 22 được áp dụng sau khi nhóm.
Mẹo thực tế
Nếu bạn cần lọc dòng, dùng WHERE. Ví dụ: Tìm tất cả nhân viên có lương trên 5000.
SELECT *
FROM employees
WHERE salary > 5000;
Nếu bạn cần lọc nhóm, dùng HAVING. Ví dụ: Tìm các phòng ban có tổng lương trên 100 000.
SELECT department, SUM(salary) AS total_salary
FROM employees
GROUP BY department
HAVING SUM(salary) > 100000;
Kết hợp WHERE và HAVING cho điều kiện phức tạp.
Ví dụ: Tìm các quốc gia có dân số trên 10 triệu, chỉ tính các thành phố có dân số trên 1 triệu.
SELECT country, SUM(population) AS total_population
FROM cities
WHERE population > 1000000
GROUP BY country
HAVING SUM(population) > 10000000;
Lỗi thường gặp và cách xử lý
Một trong những lỗi phổ biến nhất là nhầm lẫn giữa WHERE và HAVING. Ví dụ, thử dùng hàm tổng hợp trong WHERE:
SELECT department, COUNT(*)
FROM students
WHERE COUNT(*) > 10
GROUP BY department;
Truy vấn này sẽ báo lỗi vì không thể tính aggregate ở bước WHERE. Cách đúng là dùng HAVING:
SELECT department, COUNT(*)
FROM students
GROUP BY department
HAVING COUNT(*) > 10;
Một lỗi nữa là chọn sai điều kiện cho WHERE. Ví dụ:
SELECT department, AVG(age) AS avg_age
FROM students
WHERE avg_age > 20
GROUP BY department;
Ở đây điều kiện avg_age > 20 là sai, vì avg_age chưa được tính ở bước này. Giải pháp là chuyển điều kiện này sang HAVING:
SELECT department, AVG(age) AS avg_age
FROM students
GROUP BY department
HAVING AVG(age) > 20;
Hy vọng giờ bạn đã hiểu rõ sự khác biệt giữa WHERE và HAVING, cách dùng đúng và tránh lỗi thường gặp. Kiến thức này sẽ rất hữu ích khi bạn viết báo cáo phức tạp, lọc dữ liệu để phân tích và tối ưu truy vấn. Nói chung là cho phần lớn các truy vấn SQL thực tế mà bạn sẽ viết :)
GO TO FULL VERSION