Tìm hiểu sự khác biệt giữa HAVING và WHERE trong SQL
Trong SQL, WHERE và HAVING đều được dùng để lọc dữ liệu. Vì cùng có nhiệm vụ lọc nên hai mệnh đề này rất dễ bị nhầm lẫn, đặc biệt khi bắt đầu sử dụng GROUP BY và các hàm tổng hợp như COUNT(), SUM() hay AVG().
Điểm khác biệt quan trọng nhất có thể nhớ là:
WHERE lọc từng dòng dữ liệu, còn HAVING lọc các nhóm dữ liệu sau khi GROUP BY.
Để hiểu rõ hơn, hãy cùng sử dụng một bảng orders đơn giản:
| id | customer | product | amount |
|---|---|---|---|
| 1 | An | Laptop | 1000 |
| 2 | An | Mouse | 50 |
| 3 | Bình | Laptop | 1000 |
| 4 | Bình | Keyboard | 100 |
| 5 | Cường | Mouse | 50 |
1. WHERE dùng để làm gì?
WHERE được sử dụng để lọc các dòng dữ liệu dựa trên điều kiện cụ thể.
Ví dụ, lấy những đơn hàng có giá trị lớn hơn 100:
SELECT *
FROM orders
WHERE amount > 100;
Kết quả:
| id | customer | product | amount |
|---|---|---|---|
| 1 | An | Laptop | 1000 |
| 3 | Bình | Laptop | 1000 |
Ở đây, SQL kiểm tra từng dòng:
- Đơn hàng 1 →
1000 > 100→ giữ lại - Đơn hàng 2 →
50 > 100→ loại - Đơn hàng 3 →
1000 > 100→ giữ lại - Đơn hàng 4 →
100 > 100→ loại - Đơn hàng 5 →
50 > 100→ loại
WHERE đang làm việc với từng row riêng lẻ, chưa có khái niệm về nhóm dữ liệu.
2. HAVING dùng để làm gì?
HAVING được sử dụng để lọc kết quả sau khi dữ liệu đã được nhóm bằng GROUP BY.
Ví dụ, cần tìm những khách hàng có tổng giá trị đơn hàng lớn hơn 1000:
SELECT customer, SUM(amount) AS total_amount
FROM orders
GROUP BY customer
HAVING SUM(amount) > 1000;
Trước tiên, GROUP BY nhóm các đơn hàng theo khách hàng:
| customer | SUM(amount) |
|---|---|
| An | 1050 |
| Bình | 1100 |
| Cường | 50 |
Sau đó HAVING lọc các nhóm:
An → 1050 > 1000 → giữ
Bình → 1100 > 1000 → giữ
Cường → 50 > 1000 → loại
Kết quả cuối cùng:
| customer | total_amount |
|---|---|
| An | 1050 |
| Bình | 1100 |
Điểm quan trọng ở đây là HAVING không còn lọc từng đơn hàng. Nó đang lọc kết quả của từng nhóm khách hàng.
3. Vì sao WHERE và HAVING không thể thay thế cho nhau?
Sự khác biệt này liên quan trực tiếp đến thứ tự xử lý logic của SQL.
Có thể hình dung đơn giản:
FROM
↓
WHERE
↓
GROUP BY
↓
HAVING
↓
SELECT
↓
ORDER BY
WHERE được xử lý trước GROUP BY, trong khi HAVING được xử lý sau GROUP BY. Vì vậy, tại thời điểm WHERE hoạt động, các giá trị như SUM() hoặc COUNT() theo từng nhóm chưa được tạo ra.
Ví dụ sau là không hợp lệ:
SELECT customer, COUNT(*) AS total_orders
FROM orders
WHERE COUNT(*) > 1
GROUP BY customer;
Lý do là COUNT(*) cần dữ liệu đã được nhóm, nhưng WHERE được xử lý trước GROUP BY.
Cách đúng là:
SELECT customer, COUNT(*) AS total_orders
FROM orders
GROUP BY customer
HAVING COUNT(*) > 1;
Kết quả:
| customer | total_orders |
|---|---|
| An | 2 |
4. WHERE và HAVING có thể dùng cùng nhau không?
Có. Đây cũng là trường hợp rất thường gặp trong thực tế.
Ví dụ:
Tìm những khách hàng có các đơn hàng trên
100, sau đó chỉ lấy những khách hàng có tổng giá trị các đơn hàng đó lớn hơn1000.
Có thể viết:
SELECT customer, SUM(amount) AS total_amount
FROM orders
WHERE amount > 100
GROUP BY customer
HAVING SUM(amount) > 1000;
Có thể hình dung quá trình xử lý như sau:
5 orders
↓
WHERE amount > 100
↓
2 orders
↓
GROUP BY customer
↓
2 groups
↓
HAVING SUM(amount) > 1000
↓
Kết quả cuối cùng
Ở đây:
WHEREloại bỏ những đơn hàng không thỏa điều kiện.GROUP BYgom các đơn hàng còn lại theo khách hàng.HAVINGtiếp tục loại bỏ những nhóm khách hàng không đạt điều kiện.
Đây chính là lý do WHERE và HAVING có thể xuất hiện cùng trong một câu SQL.
5. Một lỗi thường gặp
Một lỗi khá phổ biến là dùng HAVING cho điều kiện không liên quan đến hàm tổng hợp.
Ví dụ:
SELECT customer, COUNT(*) AS total_orders
FROM orders
GROUP BY customer
HAVING customer = 'An';
Câu lệnh này có thể chạy, nhưng nếu mục đích chỉ là lọc khách hàng An, cách hợp lý hơn là:
SELECT customer, COUNT(*) AS total_orders
FROM orders
WHERE customer = 'An'
GROUP BY customer;
Tại sao?
Vì WHERE có thể loại bỏ dữ liệu ngay từ đầu, trước khi GROUP BY thực hiện việc nhóm dữ liệu. Điều này giúp giảm lượng dữ liệu cần xử lý.
Có thể nhớ đơn giản:
Lọc row → WHERE
Lọc group → HAVING
6. WHERE và HAVING khác nhau thế nào?
| Tiêu chí | WHERE | HAVING |
|---|---|---|
| Dùng để lọc | Row | Group |
| Thời điểm xử lý | Trước GROUP BY | Sau GROUP BY |
Dùng với GROUP BY | Không bắt buộc | Thường đi cùng |
Dùng COUNT(), SUM(), AVG() | Không | Có |
| Mục đích phổ biến | Lọc dữ liệu ban đầu | Lọc kết quả tổng hợp |
Một cách nhớ nhanh:
WHERE → "Dòng nào được giữ lại?"
HAVING → "Nhóm nào được giữ lại?"
7. Khi nào nên dùng WHERE, khi nào nên dùng HAVING?
Bạn có thể tự hỏi một câu rất đơn giản:
Điều kiện đang áp dụng cho từng dòng hay cho kết quả của một nhóm?
Nếu là từng dòng → WHERE
WHERE amount > 100
WHERE customer = 'An'
WHERE product = 'Laptop'
Nếu là kết quả của nhóm → HAVING
HAVING COUNT(*) > 2
HAVING SUM(amount) > 1000
HAVING AVG(amount) > 500
8. Kết luận
WHERE và HAVING đều có nhiệm vụ lọc dữ liệu nhưng chúng hoạt động ở hai giai đoạn khác nhau:
WHERE
↓
Lọc từng row
↓
GROUP BY
↓
Tạo các group
↓
HAVING
↓
Lọc các group
Nếu chỉ cần lọc dữ liệu thông thường, hãy nghĩ đến WHERE.
Nếu cần lọc dựa trên kết quả của COUNT(), SUM(), AVG()… sau khi nhóm dữ liệu, hãy nghĩ đến HAVING.
WHERE lọc row — HAVING lọc group.
Đây là nguyên tắc quan trọng nhất cần nhớ khi làm việc với GROUP BY trong SQL.


