
Foto oleh Brett Sayles via Pexels
HAVING sering disalahpahami sebagai 'WHERE versi GROUP BY'. Klausa ini dipakai untuk menyaring hasil setelah data dikelompokkan dan diagregasi, sesuatu yang tidak bisa dilakukan oleh WHERE biasa karena perbedaan mendasar dalam urutan pemrosesan query di SQL Server.
Artikel ini membahas perbedaan mendasar antara WHERE dan HAVING, kapan masing-masing harus dipakai, dan bagaimana menggabungkan keduanya untuk query yang optimal.
Mengapa WHERE Tidak Bisa Memfilter Hasil Agregat
WHERE tidak bisa mengakses hasil fungsi agregat
SQL Server memproses WHERE sebelum data dikelompokkan oleh GROUP BY, sehingga pada saat itu hasil SUM belum ada. Untuk memfilter berdasarkan hasil agregasi, HAVING harus digunakan karena ia dievaluasi setelah proses pengelompokan selesai.
Sintaks HAVING yang Benar
HAVING dievaluasi setelah GROUP BY
Menggabungkan WHERE dan HAVING dalam Satu Query
WHERE menyaring baris mentah, HAVING menyaring hasil agregat
Praktik terbaik adalah menggunakan WHERE untuk menyaring baris sebanyak mungkin sebelum pengelompokan, karena ini lebih efisien dibanding mengelompokkan seluruh data lalu baru menyaringnya dengan HAVING, sebab jumlah baris yang perlu dikelompokkan jadi lebih sedikit.
HAVING dengan Kondisi Majemuk
HAVING bisa menggabungkan beberapa kondisi agregat sekaligus
HAVING vs Subquery untuk Filter Hasil Agregat
Pendekatan subquery sebagai alternatif HAVING
Selain HAVING, hasil agregat juga bisa difilter dengan membungkus GROUP BY ke dalam subquery lalu menambahkan WHERE di luar subquery tersebut. Secara hasil, kedua pendekatan ini sering kali ekuivalen, namun HAVING umumnya lebih ringkas dan lebih mudah dibaca untuk kasus sederhana.
Subquery menjadi pilihan yang lebih masuk akal ketika filter yang dibutuhkan melibatkan kombinasi antara kolom agregat dan kolom non-agregat yang sudah dihitung sebelumnya, atau ketika hasil agregat tersebut perlu digabungkan lagi dengan tabel lain melalui JOIN tambahan setelah pengelompokan selesai dilakukan.
Satu hal lagi yang perlu diperhatikan, HAVING juga bisa dikombinasikan dengan AND/OR untuk menggabungkan beberapa syarat agregat sekaligus, misalnya memfilter kelompok yang jumlah transaksinya lebih dari 5 DAN total nilainya melebihi angka tertentu. Kombinasi ini sering dipakai dalam laporan yang ingin menyoroti pelanggan dengan aktivitas tinggi sekaligus nilai transaksi besar, tanpa perlu menulis query terpisah untuk masing-masing kondisi.
Kombinasi WHERE dan HAVING dalam Satu Query
WHERE menyaring baris mentah, HAVING menyaring hasil agregat
WHERE dan HAVING sering disalahpahami sebagai dua klausa yang saling menggantikan, padahal keduanya bekerja pada tahap yang berbeda dan bisa dipakai bersamaan dalam satu query seperti contoh di atas. WHERE menyaring baris-baris mentah sebelum proses pengelompokan GROUP BY dilakukan, sehingga hanya baris yang lolos WHERE yang ikut dihitung dalam agregat per kelompok, sementara HAVING menyaring kelompok-kelompok yang sudah terbentuk berdasarkan hasil perhitungan agregatnya.
Urutan logis inilah yang membuat HAVING satu-satunya klausa yang diizinkan merujuk langsung ke fungsi agregat seperti COUNT(*) atau SUM(TotalAmount) dalam kondisinya, karena pada saat HAVING dievaluasi, nilai agregat per kelompok sudah selesai dihitung; sedangkan WHERE tidak diperbolehkan merujuk ke fungsi agregat karena baris belum dikelompokkan sama sekali saat WHERE dievaluasi.
Menempatkan kondisi filter yang sebenarnya bisa dilakukan di WHERE justru ke dalam HAVING, misalnya HAVING OrderDate >= '2024-01-01' pada kolom non-agregat, adalah praktik yang kurang efisien karena memaksa SQL Server mengelompokkan dan menghitung agregat untuk seluruh baris terlebih dahulu sebelum baru menyaring, padahal menyaring lebih awal dengan WHERE akan mengurangi jumlah baris yang perlu diproses pada tahap pengelompokan.
Menggunakan Fungsi Agregat Bersarang dalam HAVING
Kombinasi beberapa fungsi agregat pada satu klausa HAVING
HAVING tidak terbatas pada satu kondisi tunggal berbasis satu fungsi agregat, melainkan dapat menggabungkan beberapa fungsi agregat berbeda dalam satu ekspresi logika, seperti membandingkan SUM dengan hasil perkalian AVG dan COUNT, atau mensyaratkan dua kondisi sekaligus dengan AND seperti pada contoh di atas yang mensyaratkan jumlah transaksi minimal lima sekaligus total belanja melebihi ambang tertentu.
Kompleksitas ekspresi pada HAVING tidak memengaruhi kebenaran logika selama setiap fungsi agregat yang dipakai benar-benar dihitung dari kolom yang sama atau kolom lain yang masih berada dalam konteks GROUP BY yang sama. SQL Server akan menghitung seluruh fungsi agregat yang direferensikan terlebih dahulu untuk setiap grup, baru kemudian mengevaluasi ekspresi logika HAVING terhadap nilai-nilai yang sudah dihitung tersebut, sehingga performa HAVING kompleks tidak jauh berbeda dari HAVING sederhana selama jumlah grup tidak berubah.
Ekspresi HAVING yang terlalu rumit, seperti menggabungkan lebih dari tiga atau empat fungsi agregat dalam satu kondisi, sebaiknya dipertimbangkan untuk dipecah memakai Common Table Expression, di mana hasil agregasi dihitung terlebih dahulu dengan alias yang jelas, lalu difilter pada klausa WHERE di query terluar. Pendekatan ini tidak mengubah hasil akhir, namun secara signifikan meningkatkan keterbacaan query terutama saat logika bisnis di baliknya perlu dijelaskan atau diaudit oleh orang lain, termasuk saat laporan tersebut diwariskan ke developer baru yang belum familier dengan konteksnya.
Menyaring Hasil Agregat dengan CTE dan WHERE sebagai Alternatif HAVING
CTE sebagai alternatif HAVING
Menyaring hasil agregat sebenarnya tidak harus selalu memakai HAVING; membungkus query GROUP BY ke dalam Common Table Expression lalu menyaringnya dengan WHERE pada query luar menghasilkan output yang persis sama, karena pada tahap ini kolom agregat seperti JumlahPesanan dan TotalBelanja sudah menjadi kolom biasa pada hasil CTE, bukan lagi fungsi agregat yang terikat aturan HAVING.
Pendekatan CTE lebih disukai ketika kondisi penyaringan cukup kompleks atau perlu dipakai berulang pada beberapa bagian query yang sama, karena memberi nama eksplisit pada setiap kolom hasil agregat sebelum disaring, membuat query lebih mudah dibaca dibanding ekspresi agregat panjang yang langsung ditulis di dalam HAVING. Dari sisi performa, SQL Server umumnya menghasilkan rencana eksekusi yang setara untuk kedua pendekatan ini.
Kesalahan Umum yang Sering Terjadi
Mencoba memfilter hasil agregat memakai WHERE
SQL Server akan langsung menolak query dengan error karena fungsi agregat belum selesai dihitung pada tahap WHERE dievaluasi, sebuah kesalahan sintaks yang sangat umum bagi pemula.
Memakai HAVING untuk menyaring baris mentah yang sebenarnya bisa disaring WHERE
Menyaring baris mentah lewat HAVING (setelah seluruh data dikelompokkan) jauh kurang efisien dibanding menyaringnya lebih dulu lewat WHERE sebelum proses pengelompokan dimulai.
Kapan Pendekatan Ini Tidak Cocok Dipakai
Pada tabel yang sangat besar, menunda penyaringan ke HAVING alih-alih WHERE bisa memaksa SQL Server mengelompokkan jutaan baris yang sebenarnya tidak relevan sebelum akhirnya disaring, membuang resource komputasi yang seharusnya bisa dihemat dengan menyaring baris mentah sedini mungkin lewat WHERE.
Ringkasan
Perbedaan utama WHERE dan HAVING terletak pada kapan masing-masing dievaluasi: WHERE menyaring baris mentah sebelum GROUP BY, sementara HAVING menyaring hasil kelompok setelah agregasi selesai dihitung.
Kebiasaan menyaring data sedini mungkin dengan WHERE sebelum menggunakan HAVING untuk hasil agregat akan membuat query jauh lebih efisien, terutama pada tabel dengan volume data besar.
Seri Belajar SQL Server:
← Sebelumnya: Mengelompokkan Data dengan GROUP BY
→ Selanjutnya: Fungsi Agregat COUNT SUM AVG MIN MAX (segera terbit)