Bedanya WHERE dan HAVING
Pertanyaan
"Apa bedanya
WHEREdanHAVING? Tunjukkan dengan contoh: cari merchant yang total transaksi sukses-nya minimal Rp150.000."
Tabel transaksi:
| transaksi_id | merchant | status | nominal |
|---|---|---|---|
| 1 | Kopi Senja | sukses | 45000 |
| 2 | Kopi Senja | gagal | 300000 |
| 3 | Warung Bu Siti | sukses | 250000 |
| 4 | Kopi Senja | sukses | 60000 |
| 5 | Warung Bu Siti | sukses | 300000 |
| 6 | Toko Budi | sukses | 150000 |
| 7 | Toko Budi | gagal | 500000 |
| 8 | Kopi Senja | sukses | 40000 |
Jawaban Singkat
WHERE menyaring baris sebelum dikelompokkan, jadi tidak bisa memakai fungsi agregat. HAVING menyaring kelompok setelah GROUP BY, jadi dipakai untuk syarat seperti SUM(...) >= 150000 atau COUNT(*) > 3. Di soal ini, syarat "status sukses" masuk WHERE, sedangkan syarat "total minimal 150 ribu" masuk HAVING.
Pembahasan
WHERE status = 'sukses'dijalankan duluan, per baris. Transaksi 2 dan 7 (gagal) dibuang sebelum dijumlahkan.GROUP BY merchantmengelompokkan sisa baris: Kopi Senja (45000 + 60000 + 40000 = 145000), Warung Bu Siti (550000), Toko Budi (150000).HAVING SUM(nominal) >= 150000menyaring kelompok. Kopi Senja (145000) gugur.
Perhatikan apa yang terjadi kalau syarat status dilupakan: Kopi Senja jadi 445000 dan Toko Budi jadi 650000 karena transaksi gagal ikut terhitung. Ketiga merchant lolos, padahal itu salah secara bisnis. Jadi WHERE dan HAVING bukan pilihan "salah satu", tapi sering dipakai bersama.
Aturan praktis:
| Syarat menyangkut... | Taruh di |
|---|---|
| Nilai kolom per baris (status, tanggal, kota) | WHERE |
| Hasil agregat (SUM, COUNT, AVG) | HAVING |
Query
SELECT merchant, COUNT(*) AS jumlah_transaksi, SUM(nominal) AS total_nominal FROM transaksi WHERE status = 'sukses' GROUP BY merchant HAVING SUM(nominal) >= 150000 ORDER BY total_nominal DESC;
Contoh Output
| merchant | jumlah_transaksi | total_nominal |
|---|---|---|
| Warung Bu Siti | 2 | 550000 |
| Toko Budi | 1 | 150000 |
Jebakan Umum
- Menulis
WHERE SUM(nominal) >= 150000. Error: agregat tidak boleh diWHEREkarenaWHEREdiproses sebelumGROUP BY. - Menaruh syarat baris biasa di
HAVING, misalnyaHAVING merchant <> 'Toko Budi'. Hasilnya sama, tapi database mengelompokkan baris yang sebenarnya bisa dibuang lebih awal. Filter sedini mungkin. - Memindahkan syarat status ke
HAVING status = 'sukses'. Error, karenastatusbukan kolomGROUP BYdan bukan agregat. Syarat per baris harus diWHEREsupaya baris gagal tidak ikut dijumlahkan. - Mengira
HAVINGwajib adaGROUP BY. TanpaGROUP BY, seluruh tabel dianggap satu kelompok.
Follow-up dari Interviewer
- "Bisa dapat hasil yang sama tanpa
HAVING?" Bisa, dengan subquery atau CTE: hitung agregat dulu, lalu filter diWHEREquery luar. - "Kalau mau tetap menghitung transaksi gagal sebagai kolom terpisah?" Pakai agregasi bersyarat:
SUM(CASE WHEN status = 'gagal' THEN nominal ELSE 0 END)atauSUM(nominal) FILTER (WHERE status = 'gagal'). - "Mana yang lebih cepat?" Filter di
WHEREbiasanya lebih murah karena mengurangi baris sebelum agregasi. Optimizer modern sering memindahkan syarat non-agregat dariHAVINGkeWHEREsecara otomatis, tapi jangan bergantung pada itu.
Yang Sebenarnya Diuji
Pemahaman urutan eksekusi logis (WHERE sebelum GROUP BY, HAVING sesudahnya) dan kemampuan memetakan syarat bisnis ke klausa yang tepat.
Catatan Dialect
FILTER (WHERE ...)di dalam agregat tersedia di DuckDB dan PostgreSQL. Di MySQL dan BigQuery pakaiCASE WHEN.- MySQL, BigQuery, dan DuckDB mengizinkan alias di
HAVING(misalnyaHAVING total_nominal >= 150000); PostgreSQL tidak, jadi tulis ulang ekspresi agregatnya.