Fungsi GROUP BY dan HAVING di SQL: Panduan Lengkap
TL;DR
GROUP BY buat mengelompokkan data berdasarkan kolom tertentu, HAVING buat filter hasil agregasi. WHERE filter sebelum grouping, HAVING filter sesudah.
Apa Itu GROUP BY di SQL?
Pernah bingung gimana caranya ngitung total penjualan per provinsi? Atau pengen tau rata-rata order per kategori produk? Nah, di sinilah GROUP BY jadi penyelamat kamu.
GROUP BY itu perintah SQL yang dipakai buat mengelompokkan baris data berdasarkan satu atau lebih kolom. Setelah dikelompokkan, kamu bisa pakai fungsi agregasi kayak SUM, COUNT, AVG, MAX, atau MIN untuk ngitung sesuatu dari tiap kelompok.
Bayangin kamu punya tumpukan nota penjualan. Terus kamu mau pisahin berdasarkan provinsi, dan hitung total penjualan masing-masing. Itulah basically yang GROUP BY lakuin.
Kenapa GROUP BY Penting Banget?
Sebagai Data Analyst, kamu pasti sering banget dapet request kayak gini:
- "Berapa total penjualan per bulan?"
- "Produk mana yang paling laku di tiap provinsi?"
- "Rata-rata nilai order per kategori berapa sih?"
Semua pertanyaan itu butuh GROUP BY. Tanpa GROUP BY, kamu cuma bisa dapet satu angka total dari seluruh data. Tapi dengan GROUP BY, kamu bisa breakdown datanya jadi insight yang lebih meaningful.
Dataset yang Akan Kita Pakai
Buat tutorial ini, kita pakai dataset penjualan UMKM Indonesia. Bayangin kamu jadi Data Analyst di platform marketplace yang bantu UMKM jualan online.
Tabel: penjualan
| id | tanggal | provinsi | kategori | produk | qty | harga_satuan | total |
|---|---|---|---|---|---|---|---|
| 1 | 2024-01-15 | Jawa Barat | Makanan | Keripik Singkong | 50 | 15000 | 750000 |
| 2 | 2024-01-15 | Jawa Tengah | Kerajinan | Batik Tulis | 5 | 250000 | 1250000 |
| 3 | 2024-01-16 | Jawa Barat | Makanan | Dodol Garut | 30 | 25000 | 750000 |
| 4 | 2024-01-16 | Bali | Kerajinan | Ukiran Kayu | 3 | 500000 | 1500000 |
| 5 | 2024-01-17 | Jawa Barat | Fashion | Kaos Distro | 20 | 85000 | 1700000 |
| 6 | 2024-01-17 | Jawa Tengah | Makanan | Lumpia Semarang | 40 | 20000 | 800000 |
| 7 | 2024-01-18 | Bali | Makanan | Pie Susu | 100 | 8000 | 800000 |
| 8 | 2024-01-18 | Yogyakarta | Kerajinan | Perak Kotagede | 10 | 150000 | 1500000 |
| 9 | 2024-01-19 | Jawa Barat | Makanan | Keripik Singkong | 60 | 15000 | 900000 |
| 10 | 2024-01-19 | Jawa Tengah | Fashion | Batik Cap | 15 | 120000 | 1800000 |
Tabel: umkm
| id | nama_usaha | provinsi | kategori | tahun_berdiri |
|---|---|---|---|---|
| 1 | Keripik Bu Siti | Jawa Barat | Makanan | 2018 |
| 2 | Batik Sekar | Jawa Tengah | Kerajinan | 2015 |
| 3 | Dodol Pak Asep | Jawa Barat | Makanan | 2020 |
| 4 | Bali Craft | Bali | Kerajinan | 2019 |
| 5 | Urban Threads | Jawa Barat | Fashion | 2021 |
Sintaks Dasar GROUP BY
Ini struktur dasar GROUP BY:
SELECT kolom_grouping, FUNGSI_AGREGASI(kolom)
FROM nama_tabel
WHERE kondisi
GROUP BY kolom_grouping;
Yang perlu kamu inget:
1. Kolom yang muncul di SELECT (yang bukan fungsi agregasi) HARUS ada di GROUP BY
2. GROUP BY dijalankan SETELAH WHERE
3. Kamu bisa GROUP BY lebih dari satu kolom
Contoh 1: Total Penjualan per Provinsi
Ini contoh paling basic. Kita mau tau total penjualan di tiap provinsi.
SELECT
provinsi,
SUM(total) AS total_penjualan
FROM penjualan
GROUP BY provinsi;
Hasil:
| provinsi | total_penjualan |
|---|---|
| Jawa Barat | 4100000 |
| Jawa Tengah | 3850000 |
| Bali | 2300000 |
| Yogyakarta | 1500000 |
Keren kan? Dengan satu query, kamu bisa langsung liat provinsi mana yang penjualannya paling gede.
Contoh 2: Jumlah Transaksi dan Rata-rata per Kategori
Sekarang kita mau liat performa tiap kategori produk.
SELECT
kategori,
COUNT(*) AS jumlah_transaksi,
SUM(total) AS total_penjualan,
AVG(total) AS rata_rata_order
FROM penjualan
GROUP BY kategori;
Hasil:
| kategori | jumlah_transaksi | total_penjualan | rata_rata_order |
|---|---|---|---|
| Makanan | 5 | 4000000 | 800000 |
| Kerajinan | 3 | 4250000 | 1416667 |
| Fashion | 2 | 3500000 | 1750000 |
Dari sini keliatan bahwa walaupun Makanan punya transaksi paling banyak, tapi rata-rata nilai ordernya paling kecil. Sementara Fashion punya rata-rata order tertinggi.
Contoh 3: GROUP BY dengan Multiple Columns
Kamu juga bisa grouping berdasarkan lebih dari satu kolom. Misalnya, kita mau liat penjualan per provinsi DAN per kategori.
SELECT
provinsi,
kategori,
SUM(total) AS total_penjualan
FROM penjualan
GROUP BY provinsi, kategori
ORDER BY provinsi, total_penjualan DESC;
Hasil:
| provinsi | kategori | total_penjualan |
|---|---|---|
| Bali | Kerajinan | 1500000 |
| Bali | Makanan | 800000 |
| Jawa Barat | Fashion | 1700000 |
| Jawa Barat | Makanan | 2400000 |
| Jawa Tengah | Fashion | 1800000 |
| Jawa Tengah | Kerajinan | 1250000 |
| Jawa Tengah | Makanan | 800000 |
| Yogyakarta | Kerajinan | 1500000 |
Nah, sekarang kamu bisa liat breakdown yang lebih detail. Jawa Barat ternyata paling kuat di kategori Makanan.
Apa Itu HAVING?
Oke, sekarang bayangin kamu mau filter hasil GROUP BY. Misalnya, kamu cuma mau liat provinsi yang total penjualannya di atas 2 juta.
Banyak yang mikir, "Ya udah pake WHERE aja dong!"
Tapi tunggu dulu. WHERE itu filter SEBELUM data di-group. Jadi kamu ga bisa nulis kayak gini:
-- INI SALAH!
SELECT provinsi, SUM(total) AS total_penjualan
FROM penjualan
WHERE SUM(total) > 2000000 -- Error!
GROUP BY provinsi;
Query di atas bakal error karena WHERE ga bisa pake fungsi agregasi. Di sinilah HAVING masuk.
HAVING itu filter yang dijalankan SETELAH GROUP BY. Jadi kamu bisa filter berdasarkan hasil agregasi.
Sintaks HAVING
SELECT kolom_grouping, FUNGSI_AGREGASI(kolom)
FROM nama_tabel
WHERE kondisi_per_baris
GROUP BY kolom_grouping
HAVING kondisi_agregasi;
Urutan eksekusi:
1. FROM - ambil data dari tabel
2. WHERE - filter baris individual
3. GROUP BY - kelompokkan data
4. HAVING - filter hasil grouping
5. SELECT - pilih kolom yang mau ditampilkan
6. ORDER BY - urutkan hasil
Contoh 4: HAVING untuk Filter Agregasi
Sekarang kita coba filter provinsi dengan total penjualan di atas 2 juta.
SELECT
provinsi,
SUM(total) AS total_penjualan
FROM penjualan
GROUP BY provinsi
HAVING SUM(total) > 2000000;
Hasil:
| provinsi | total_penjualan |
|---|---|
| Jawa Barat | 4100000 |
| Jawa Tengah | 3850000 |
| Bali | 2300000 |
Yogyakarta ga masuk karena total penjualannya cuma 1.5 juta.
Contoh 5: Kombinasi WHERE dan HAVING
Ini yang sering bikin bingung. Kapan pake WHERE, kapan pake HAVING?
Inget aturan ini:
- WHERE: filter baris SEBELUM grouping (filter per record)
- HAVING: filter SETELAH grouping (filter hasil agregasi)
Misalnya, kamu mau liat total penjualan per kategori, tapi:
1. Cuma untuk transaksi di bulan Januari 2024
2. Dan cuma kategori yang total penjualannya di atas 1 juta
SELECT
kategori,
SUM(total) AS total_penjualan
FROM penjualan
WHERE tanggal BETWEEN '2024-01-01' AND '2024-01-31'
GROUP BY kategori
HAVING SUM(total) > 1000000;
Hasil:
| kategori | total_penjualan |
|---|---|
| Makanan | 4000000 |
| Kerajinan | 4250000 |
| Fashion | 3500000 |
Di sini, WHERE filter dulu transaksi yang di Januari, baru di-group dan difilter pake HAVING.
Contoh 6: HAVING dengan COUNT
HAVING juga sering dipake bareng COUNT. Misalnya, kamu mau cari provinsi yang punya minimal 3 transaksi.
SELECT
provinsi,
COUNT(*) AS jumlah_transaksi
FROM penjualan
GROUP BY provinsi
HAVING COUNT(*) >= 3;
Hasil:
| provinsi | jumlah_transaksi |
|---|---|
| Jawa Barat | 4 |
| Jawa Tengah | 3 |
Bali dan Yogyakarta ga masuk karena transaksinya kurang dari 3.
Contoh 7: Multiple Conditions di HAVING
Kamu bisa pakai AND dan OR di HAVING, sama kayak di WHERE.
SELECT
kategori,
COUNT(*) AS jumlah_transaksi,
SUM(total) AS total_penjualan,
AVG(total) AS rata_rata
FROM penjualan
GROUP BY kategori
HAVING COUNT(*) >= 2 AND AVG(total) > 1000000;
Hasil:
| kategori | jumlah_transaksi | total_penjualan | rata_rata |
|---|---|---|---|
| Kerajinan | 3 | 4250000 | 1416667 |
| Fashion | 2 | 3500000 | 1750000 |
Query ini cari kategori yang punya minimal 2 transaksi DAN rata-rata order di atas 1 juta.
Contoh 8: GROUP BY dengan Date Functions
Ini sering banget dipake buat bikin laporan periodik. Misalnya, total penjualan per minggu.
SELECT
DATE_TRUNC('week', tanggal) AS minggu,
SUM(total) AS total_penjualan
FROM penjualan
GROUP BY DATE_TRUNC('week', tanggal)
ORDER BY minggu;
Atau kalau kamu mau per bulan:
SELECT
EXTRACT(YEAR FROM tanggal) AS tahun,
EXTRACT(MONTH FROM tanggal) AS bulan,
SUM(total) AS total_penjualan
FROM penjualan
GROUP BY
EXTRACT(YEAR FROM tanggal),
EXTRACT(MONTH FROM tanggal)
ORDER BY tahun, bulan;
Contoh 9: GROUP BY dengan JOIN
Sekarang kita gabungin tabel penjualan dengan tabel umkm. Misalnya, kita mau tau total penjualan per tahun berdiri UMKM.
SELECT
u.tahun_berdiri,
COUNT(DISTINCT u.id) AS jumlah_umkm,
SUM(p.total) AS total_penjualan
FROM penjualan p
JOIN umkm u ON p.provinsi = u.provinsi AND p.kategori = u.kategori
GROUP BY u.tahun_berdiri
ORDER BY u.tahun_berdiri;
Ini bisa ngasih insight menarik, misalnya UMKM yang udah lama berdiri penjualannya gimana dibanding yang baru.
Common Mistakes yang Harus Dihindari
Mistake 1: Kolom Non-Agregat ga Ada di GROUP BY
-- SALAH
SELECT provinsi, kategori, SUM(total)
FROM penjualan
GROUP BY provinsi; -- kategori ga di-include
Ini bakal error. Semua kolom yang bukan fungsi agregasi HARUS ada di GROUP BY.
Mistake 2: Pake WHERE untuk Filter Agregasi
-- SALAH
SELECT provinsi, SUM(total)
FROM penjualan
WHERE SUM(total) > 1000000 -- Error!
GROUP BY provinsi;
Pake HAVING, bukan WHERE, untuk filter hasil agregasi.
Mistake 3: Salah Urutan Clause
-- SALAH
SELECT provinsi, SUM(total)
FROM penjualan
HAVING SUM(total) > 1000000
GROUP BY provinsi; -- HAVING sebelum GROUP BY
Urutan yang bener: SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY.
Mistake 4: Lupa NULL Values
NULL itu dikelompokkan jadi satu grup tersendiri. Kadang ini bikin hasil query jadi unexpected. Pake COALESCE kalau perlu handle NULL.
SELECT
COALESCE(provinsi, 'Tidak Diketahui') AS provinsi,
SUM(total) AS total_penjualan
FROM penjualan
GROUP BY provinsi;
Tips Praktis buat Data Analyst
1. Aliasing Hasil Agregasi
Selalu kasih alias yang descriptive ke hasil agregasi. Ini bikin query lebih readable dan hasilnya lebih gampang dipahami.
SELECT
kategori,
SUM(total) AS total_pendapatan,
AVG(total) AS rata_rata_transaksi,
MAX(total) AS transaksi_terbesar
FROM penjualan
GROUP BY kategori;
2. Pake ORDER BY buat Sorting
Hasil GROUP BY biasanya lebih berguna kalau di-sort.
SELECT
provinsi,
SUM(total) AS total_penjualan
FROM penjualan
GROUP BY provinsi
ORDER BY total_penjualan DESC; -- Dari yang terbesar
3. ROUND untuk Angka Decimal
Rata-rata sering menghasilkan angka decimal panjang. Pake ROUND biar lebih clean.
SELECT
kategori,
ROUND(AVG(total), 2) AS rata_rata
FROM penjualan
GROUP BY kategori;
4. Combine dengan CASE WHEN
Kamu bisa bikin grouping yang lebih custom pake CASE WHEN.
SELECT
CASE
WHEN total < 1000000 THEN 'Small'
WHEN total < 2000000 THEN 'Medium'
ELSE 'Large'
END AS order_size,
COUNT(*) AS jumlah
FROM penjualan
GROUP BY
CASE
WHEN total < 1000000 THEN 'Small'
WHEN total < 2000000 THEN 'Medium'
ELSE 'Large'
END;
Kapan Pake GROUP BY vs Subquery?
Kadang ada yang bingung, mending pake GROUP BY atau subquery. Aturan simpelnya:
- GROUP BY: Kalau kamu mau agregasi dan tampilin hasilnya langsung
- Subquery: Kalau hasil agregasi mau dipake buat filter atau join dengan data lain
Contoh pake subquery:
-- Cari transaksi yang nilainya di atas rata-rata
SELECT *
FROM penjualan
WHERE total > (
SELECT AVG(total) FROM penjualan
);
Kesimpulan
Nah, sekarang kamu udah paham kan gimana caranya pake GROUP BY dan HAVING? Inget poin-poin ini:
- GROUP BY mengelompokkan data berdasarkan kolom tertentu
- HAVING filter hasil agregasi (setelah GROUP BY)
- WHERE filter baris individual (sebelum GROUP BY)
- Semua kolom non-agregat di SELECT HARUS ada di GROUP BY
- Urutan: FROM > WHERE > GROUP BY > HAVING > SELECT > ORDER BY
Latihan itu kunci. Coba bikin query sendiri pake dataset yang kamu punya. Mulai dari yang simple kayak COUNT dan SUM per kategori, terus lanjut ke yang lebih kompleks.
Happy querying! Kalau ada pertanyaan, langsung aja praktek di nguliksql.id ya!
Artikel Terkait
Cara Menggunakan Subquery di SQL (Dengan 10 Contoh Praktis)
Pelajari cara pakai subquery di SQL dengan 10 contoh praktis menggunakan dataset e-commerce Indonesia
Perbedaan INNER JOIN, LEFT JOIN, RIGHT JOIN, dan FULL JOIN di SQL
Belajar perbedaan jenis-jenis JOIN di SQL dengan contoh praktis dan visualisasi. Panduan lengkap INNER, LEFT, RIGHT, dan FULL JOIN untuk pemula.
Cara Install PostgreSQL di Windows, Mac, dan Linux (Panduan Lengkap)
Panduan step-by-step install PostgreSQL di Windows, macOS, dan Linux lengkap dengan pgAdmin dan troubleshooting