HAVING: Menyaring Hasil Kelompok (dan Beda dengan WHERE)
Menyaring kelompok setelah dihitung dengan HAVING, memahami perbedaan WHERE vs HAVING, dan menyusun query agregat lengkap dengan urutan klausa yang benar.
Masalah: WHERE tidak bisa menyaring hasil agregat
Misalkan kita mau: "Tampilkan kota yang punya minimal 2 pelanggan." Kita coba:
SELECT kota, COUNT(*) AS jumlah
FROM pelanggan
WHERE COUNT(*) >= 2 -- ERROR: Invalid use of group function
GROUP BY kota;
Gagal, karena saat WHERE bekerja, kelompok belum terbentuk dan COUNT belum dihitung. Kita butuh saringan yang bekerja sesudah pengelompokan: HAVING.
SELECT kota, COUNT(*) AS jumlah
FROM pelanggan
GROUP BY kota
HAVING COUNT(*) >= 2;
+----------+--------+
| kota | jumlah |
+----------+--------+
| Jakarta | 3 |
| Bandung | 2 |
| Surabaya | 2 |
+----------+--------+
WHERE vs HAVING: satu kalimat
| WHERE | HAVING | |
|---|---|---|
| Menyaring | Baris individual | Kelompok hasil GROUP BY |
| Bekerja | Sebelum GROUP BY | Sesudah GROUP BY & agregat |
| Boleh pakai agregat? | Tidak | Ya |
| Contoh | WHERE status = 'lunas' | HAVING COUNT(*) >= 2 |
Aturan efisien: kalau syaratnya bisa ditulis dengan WHERE (tidak butuh hasil agregat), tulislah di WHERE. Menyaring baris lebih awal membuat MySQL mengelompokkan lebih sedikit data, jadi lebih cepat.
Menggabungkan WHERE dan HAVING
-- pelanggan yang punya minimal 2 pesanan LUNAS
SELECT pelanggan_id, COUNT(*) AS jumlah_lunas
FROM pesanan
WHERE status = 'lunas' -- 1) buang pesanan yang tidak lunas (baris)
GROUP BY pelanggan_id -- 2) kelompokkan per pelanggan
HAVING COUNT(*) >= 2 -- 3) simpan kelompok yang berisi minimal 2
ORDER BY jumlah_lunas DESC; -- 4) urutkan
Menyusun query agregat: urutan wajib
SELECT kolom, agregat
FROM tabel
WHERE syarat_baris
GROUP BY kolom
HAVING syarat_kelompok
ORDER BY ...
LIMIT ...;
Hafalkan dengan kalimat: "Setiap Fajar, Warung Gelar Hidangan, Orang Lapar" (SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT). Sedikit konyol, tapi efektif.
Contoh lain
-- kategori yang rata-rata harganya di atas 15.000
SELECT kategori_id, ROUND(AVG(harga)) AS rata_harga
FROM produk
GROUP BY kategori_id
HAVING AVG(harga) > 15000;
-- pelanggan yang total belanjanya besar (di bab JOIN)
-- ... GROUP BY pelanggan HAVING SUM(subtotal) >= 100000
Di MySQL, HAVING boleh memakai alias SELECT (HAVING jumlah >= 2), tetapi menulis ulang fungsinya lebih portabel ke database lain.