sqlMahir6 mnt baca

GROUP BY dan HAVING di SQL

Agregasi per kelompok dengan GROUP BY, fungsi COUNT/SUM/AVG/MIN/MAX, dan perbedaan krusial WHERE vs HAVING lewat contoh rekap penjualan.

Kenapa GROUP BY adalah skill SQL paling dicari

Kalau SELECT dan WHERE adalah "membaca" data, GROUP BY adalah "merangkum" data. Hampir semua pertanyaan bisnis adalah pertanyaan agregasi: berapa total penjualan per kategori, rata-rata transaksi per kasir, cabang mana yang paling laris. Tanpa GROUP BY, kamu harus mengekspor ribuan baris ke Excel dan pivot manual. Dengan GROUP BY, database yang mengerjakan semuanya dalam sepersekian detik. Ini adalah pembeda antara orang yang "bisa SQL" dan orang yang "produktif dengan SQL".

Analogi: GROUP BY itu seperti menyortir kelereng

Bayangkan seember kelereng campur warna. Pertanyaan "ada berapa kelereng?" dijawab COUNT biasa. Pertanyaan "ada berapa kelereng per warna?" mengharuskan kamu mengelompokkan dulu per warna, baru menghitung tiap kelompok. Itulah GROUP BY: kelompokkan baris yang punya nilai sama, lalu terapkan fungsi agregasi ke tiap kelompok.

Contoh 1: agregasi sederhana per kategori

sql
SELECT kategori, COUNT(*) AS jumlah_transaksi
FROM penjualan
GROUP BY kategori;

Hasilnya satu baris per kategori, masing-masing dengan hitungannya. Perhatikan: semua kolom di SELECT yang bukan agregasi wajib muncul di GROUP BY. Ini aturan yang dilanggar pemula paling sering.

Contoh 2: rekap penjualan yang realistis

sql
SELECT
    kategori,
    COUNT(*) AS jumlah_transaksi,
    SUM(total_bayar) AS omzet,
    AVG(total_bayar) AS rata_rata,
    MIN(total_bayar) AS terkecil,
    MAX(total_bayar) AS terbesar
FROM penjualan
WHERE tanggal >= '2026-10-01'
GROUP BY kategori
ORDER BY omzet DESC;

Query ini menjawab "rekap penjualan per kategori bulan ini" dalam satu tembakan. WHERE memfilter baris dulu (hanya Oktober), baru datanya dikelompokkan dan diagregasi. ORDER BY omzet DESC menaruh kategori paling laris di atas supaya langsung terbaca. Lima fungsi agregasi inti (COUNT, SUM, AVG, MIN, MAX) hampir selalu muncul bersamaan di laporan nyata.

Contoh 3: HAVING untuk filter hasil agregasi

sql
SELECT kategori, SUM(total_bayar) AS omzet
FROM penjualan
WHERE tanggal >= '2026-10-01'
GROUP BY kategori
HAVING SUM(total_bayar) > 5000000
ORDER BY omzet DESC;

Query ini menjawab "kategori mana yang omzetnya di atas 5 juta". Filter SUM(total_bayar) > 5000000 tidak bisa ditulis di WHERE, karena WHERE berjalan sebelum agregasi dihitung. Di sinilah HAVING berperan: ia memfilter setelah GROUP BY, memakai hasil agregasi.

Jebakan umum 1: WHERE vs HAVING tertukar

SALAH: menulis WHERE SUM(total_bayar) > 5000000. Database akan error karena saat WHERE dievaluasi, nilai SUM belum ada. Atau lebih licik: memfilter kolom biasa di HAVING, yang memang jalan di beberapa database tapi membingungkan dan tidak portabel.

BENAR: hafalkan urutan logisnya. WHERE menyaring baris mentah sebelum dikelompokkan. HAVING menyaring kelompok sesudah agregasi. Kalau kondisinya memakai fungsi agregasi (SUM, COUNT, AVG), pasti HAVING. Kalau kondisinya soal baris individual (tanggal, status), pasti WHERE.

Jebakan umum 2: kolom non-agregasi lupa masuk GROUP BY

SALAH: SELECT kategori, kasir, SUM(total_bayar) FROM penjualan GROUP BY kategori. Kolom kasir tidak diagregasi dan tidak di-group, jadi database tidak tahu kasir mana yang mau ditampilkan untuk tiap kategori. MySQL versi lama diam-diam mengambil nilai sembarang (berbahaya), PostgreSQL langsung error (lebih jujur).

BENAR: tiap kolom di SELECT harus salah satu: masuk GROUP BY, atau dibungkus fungsi agregasi. Tidak ada pengecualian.

Catatan teknis: Urutan eksekusi logis SQL adalah FROM, WHERE, GROUP BY, HAVING, SELECT, ORDER BY. Urutan penulisan memang SELECT dulu, tapi database mengeksekusinya belakangan. Memahami urutan ini menjelaskan kenapa kamu bisa memakai alias kolom di ORDER BY tapi tidak di WHERE atau GROUP BY (di kebanyakan database).

Ringkasan cepat

  • GROUP BY: kelompokkan baris yang sama, lalu agregasi tiap kelompok.
  • Lima fungsi wajib: COUNT, SUM, AVG, MIN, MAX.
  • WHERE filter baris sebelum agregasi, HAVING filter kelompok sesudah agregasi.
  • Kolom non-agregasi di SELECT wajib ada di GROUP BY.

Tantangan

Rekap penjualan per kategori

Dari tabel penjualan (kolom: tanggal, kategori, kasir, total_bayar): tulis query yang menampilkan per kategori jumlah transaksi, omzet, dan rata-rata, hanya untuk bulan berjalan, dan hanya kategori dengan omzet di atas 1 juta, diurutkan dari omzet terbesar.